蹉跎莫遣韶光老,人生唯有读书好。这篇文章主要讲述桥接模式在大厂是如何使用的?相关的知识,希望能为你提供帮助。
1 定义Bridge Design Pattern,将抽象部分与它的实现部分分离,使之任意删减,而无需受其它约束。
Decouple an abstraction from its implementation so that the two can vary independently,将抽象和实现解耦,让它们可以独立变化。
2 结构【桥接模式在大厂是如何使用的()】
3 分析理解桥接模式,重点需要理解如何将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化。
抽象化:抽象化就是忽略一些信息,把不同的实体当作同样的实体对待。在面向对象中,将对象的共同性质抽取出来形成类的过程即为抽象化的过程。
实现化:针对抽象化给出的具体实现,就是实现化,抽象化与实现化是一对互逆的概念,实现化产生的对象比抽象化更具体,是对抽象化事物的进一步具体化的产物。
脱耦:脱耦就是将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联,将两个角色之间的继承关系改为关联关系。桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合)而不是继承,从而使两者可以相对独立地变化,这就是桥接模式的用意。
4 优点
5 缺点
6 时机基于此,以下情形可考虑此模式进行设计与实施
案例:不同类型和渠道的消息推送系统结构型模式:桥接模式。代码实现简单,但理解有难度,应用场景也局限,所以,没有那么常用。
桥接模式的原理解析也可理解成:“一个类存在两个(或多个)独立变化的维度,通过组合,让这两个(或多个)维度可独立进行扩展。”
组合替代继承,就能避免继承层次的指数级爆炸,即“组合优于继承”设计原则。
JDBC驱动桥接模式的经典应用,利用JDBC驱动来查询数据库:
Class.forName("com.mysql.jdbc.Driver");
//加载及注册JDBC驱动程序
String url = "jdbc:mysql://localhost:3306/sample_db?user=root&
password=your_password";
Connection con = DriverManager.getConnection(url);
Statement stmt = con.createStatement();
String query = "select * from test";
ResultSet rs=stmt.executeQuery(query);
while(rs.next())
rs.getString(1);
rs.getInt(2);
若想把MySQL换成Oracle,只需将第一行代码中的com.mysql.jdbc.Driver换成oracle.jdbc.driver.OracleDriver。还有更灵活实现方式,把需要加载的Driver类写到配置文件,程序启动时自动从配置文件加载,切换数据库时,就无需修改代码,仅修改配置文件然后发布即可。
不管改代码or配置,从一个DB切到另一种DB,都只需改动少代码或完全不需改代码,如此轻量级的数据库切换操作是如何实现的?
package com.mysql.jdbc;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver
static
try
java.sql.DriverManager.registerDriver(new Driver());
catch (SQLException E)
throw new RuntimeException("Cant register driver!");
/**
* Construct a new driver and register it with DriverManager
* @throws SQLException if a database error occurs.
*/
public Driver() throws SQLException
// Required for Class.forName().newInstance()
执行Class.forName(“com.mysql.jdbc.Driver”)时,其实执行了:
DriverManager干嘛的?把具体Driver实现类(如com.mysql.jdbc.Driver)注册到DriverManager后,后续所有对JDBC接口的调用,都会委派到具体Driver实现类执行。而Driver实现类都实现了相同接口(java.sql.Driver ),这也是灵活切换Driver原因。
public class DriverManager
private final static CopyOnWriteArrayList<
DriverInfo>
registeredDrivers = new CopyOnWriteArrayList<
DriverInfo>
();
//...
static
loadInitialDrivers();
println("JDBC DriverManager initialized");
//...
public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException
if (driver != null)
registeredDrivers.addIfAbsent(new DriverInfo(driver));
else
throw new NullPointerException();
public static Connection getConnection(String url, String user, String password) throws SQLException
java.util.Properties info = new java.util.Properties();
if (user != null)
info.put("user", user);
if (password != null)
info.put("password", password);
return (getConnection(url, info, Reflection.getCallerClass()));
//...
桥接模式的定义“将抽象和实现解耦,让它们可以独立变化”。JDBC案例里,啥是“抽象”?啥是“实现”?:
JDBC、Driver分别独立开发,通过对象之间的组合关系协作。JDBC所有逻辑操作,最终都委托给具体Driver执行。
桥接模式应用案例API接口监控告警的例子:根据不同告警规则,触发不同类型告警。告警支持多种通知渠道,包括:邮件、短信、微信、自动语音电话。通知紧急程度有多种类型,包括:SEVERE(严重)、URGENCY(紧急)、NORMAL(普通)、TRIVIAL(无关紧要)。不同的紧急程度对应不同的通知渠道。比如,SERVE(严重)级别的消息会通过“自动语音电话”告知责任人。
当时关于发送告警信息,只给出粗略设计,现在来实现。
最简单、最直接的实现:
public enum NotificationEmergencyLevel
SEVERE, URGENCY, NORMAL, TRIVIAL
public class Notification
private List<
String>
emailAddresses;
private List<
String>
telephones;
private List<
String>
wechatIds;
public Notification()
public void setEmailAddress(List<
String>
emailAddress)
this.emailAddresses = emailAddress;
public void setTelephones(List<
String>
telephones)
this.telephones = telephones;
public void setWechatIds(List<
String>
wechatIds)
this.wechatIds = wechatIds;
public void notify(NotificationEmergencyLevel level, String message)
if (level.equals(NotificationEmergencyLevel.SEVERE))
//...自动语音电话
else if (level.equals(NotificationEmergencyLevel.URGENCY))
//...发微信
else if (level.equals(NotificationEmergencyLevel.NORMAL))
//...发邮件
else if (level.equals(NotificationEmergencyLevel.TRIVIAL))
//...发邮件
//在API监控告警的例子中,我们如下方式来使用Notification类:
public class ErrorAlertHandler extends AlertHandler
public ErrorAlertHandler(AlertRule rule, Notification notification)
super(rule, notification);
@Override
public void check(ApiStatInfo apiStatInfo)
if (apiStatInfo.getErrorCount() >
rule.getMatchedRule(apiStatInfo.getApi()).getMaxErrorCount())
notification.notify(NotificationEmergencyLevel.SEVERE, "...");
Notification类存在大量if/else。若每个分支中的代码都不复杂,后期也没有无限膨胀的可能(增加更多if/else分支判断),那这样设计问题不大,没必要非得摒弃if/else。但Notification显然不是这样。其每个if/else代码逻辑都复杂,发送通知的所有逻辑堆在Notification类。一个类的代码越多,就越难读懂,越难修改,维护的成本也就越高。很多设计模式都是试图将庞大的类拆分成更细小的类,然后再通过某种更合理的结构组装在一起。
针对Notification,将不同渠道的发送逻辑剥离出,形成独立的消息发送类(MsgSender相关类):
二者可独立开发,通过组合关系(即桥梁)任意组合。任意组合:不同紧急程度的消息和发送渠道之间的对应关系,不是在代码中固定写死,而是可动态指定(如通过读取配置获取对应关系)。
于是开始重构:
public interface MsgSender
void send(String message);
public class TelephoneMsgSender implements MsgSender
private List<
String>
telephones;
public TelephoneMsgSender(List<
String>
telephones)
this.telephones = telephones;
@Override
public void send(String message)
//...
public class EmailMsgSender implements MsgSender
// 与TelephoneMsgSender代码结构类似,所以省略...
public class WechatMsgSender implements MsgSender
// 与TelephoneMsgSender代码结构类似,所以省略...
public abstract class Notification
protected MsgSender msgSender;
public Notification(MsgSender msgSender)
this.msgSender = msgSender;
public abstract void notify(String message);
public class SevereNotification extends Notification
public SevereNotification(MsgSender msgSender)
super(msgSender);
@Override
public void notify(String message)
msgSender.send(message);
public class UrgencyNotification extends Notification
// 与SevereNotification代码结构类似,所以省略...
public class NormalNotification extends Notification
// 与SevereNotification代码结构类似,所以省略...
public class TrivialNotification extends Notification
// 与SevereNotification代码结构类似,所以省略...
总结对该模式有如下不同理解。在GoF的《设计模式》一书中,桥接模式被定义为:“将抽象和实现解耦,让它们可以独立变化。”在其他资料和书籍中,还有另外一种更加简单的理解方式:“一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。”:
推荐阅读
- 诺!给你「最酷」网页设计指南
- Python <算法思想集结;之初窥基础算法
- FFI实战之对接GO(CGO)Delphi类型转Go
- HttpServlet源码分析
- grafana上安装zabbix插件
- PXE 高效批量网络装机
- 用一维数组计算并输出Fibonacci数列的前20项
- 字体图标转png透明图标——小程序开发用
- (0基础学Linux系列)1.07 远程连接故障