IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    设计模式之美-课程笔记30-桥接模式

    10k发表于 2023-08-28 00:00:00
    love 0

    桥接模式:如何实现支持不同类型和取到的消息推送系统

    原理

    1. Decouple an abstraction from its implementation so that the two can vary independently. 将抽象和实现解耦,让他们可以独立变化。
    2. 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);
    }
    
    1. 如果想使用Oracle数据库,只需要将第一行代码的driver换成OricalDriver即可。或者更灵活将driver写在配置文件,修改时只改配置文件。
    2. 看一下如何实现:
    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("Can't 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()
      }
    }
    
    1. 当执行Class.forName("com.mysql.jdbc.Driver")的时候,做了两件事,一是要求JVM查找并加载制定的Driver类,第二件事情是执行该类的静态代码,也就是将Driver注册到DriverManager类中。
    2. 我们再来看一下,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本身相当于抽象,一个抽象的类库, 具体的Driver相当于实现,跟具体数据库相关的类库。(这里的抽象和实现与Java中的抽象类、接口和实现类不是一个概念)。

    JDBC和Driver独立开发,通过对象之间的组合,组装在一起。JDBC的所有逻辑操作委托给Driver执行。

    img

    应用举例

    之前有个通过API接口监控告警的例子:根据不同柜子,触发不同类型的告警。线面针对发送信息做一些设计:

    1. 最基本的实现
    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, "...");
        }
      }
    }
    
    1. Notification类中有一些if-else的代码逻辑复杂。所以将不同取到的发送逻辑玻璃,形成独立的消息发送类。其中Notification类相当于抽象,MsgSend相当于实现,两者可以独立开发,通过组合关系也就是桥梁任意组合在一起。
    2. 所谓组合就是根据紧急程度配置发送方式。也不一定写死在代码,也可以放进配置文件。
    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代码结构类似,所以省略...
    }
    

    思考

    1. 还有另外一种更加简单的理解方式:“一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。”

    2. 定义中的“抽象”,指的并非“抽象类”或“接口”,而是被抽象出来的一套“类库”,它只包含骨架代码,真正的业务逻辑需要委派给定义中的“实现”来完成。而定义中的“实现”,也并非“接口的实现类”,而是一套独立的“类库”。“抽象”和“实现”独立开发,通过对象之间的组合关系,组装在一起。

      对于第二种理解方式,它非常类似我们之前讲过的“组合优于继承”设计原则,通过组合关系来替代继承关系,避免继承层次的指数级爆炸。



沪ICP备19023445号-2号
友情链接