背景 
桥接(bridge)模式首先的场景是内部实现逻辑分为多个模块,每个模块又可能对应多种实现
在桥接内部,这些不同类型的模块按照组合或聚合的方式组织在一起,将逻辑模块抽象部分与实现部分相分离
目的 
降低内部多种类型逻辑模块的耦合,扩展变成以各模块为单位,更为灵活
现实世界类比  Vans自由定制鞋_Vans(范斯)中国官方网站 
球鞋定制,从选择款式开始,对不同的部位(鞋头、鞋腰、侧边条纹、鞋带等)选择不同的设计(颜色、图案、材质等),最终产出最终产品(桥接模式的最终成品可以认为是组合了多个模块逻辑的执行器)
针对不同部位选择不同的设计,而不是平铺出笛卡尔积式的配置表,就是桥接模式希望实现的解耦的目的
实践 
模拟一个场景,比如服务内希望实现一个消息通知功能
消息分为多种记录方式:缓存、数据库
通知的方式也分为多种方式:邮件、微信
服务内定义什么级别的消息通过什么方式进行通知
问题 
如果将所需要的发送方式进行实现,需要实现 4 种执行器(2 ×
2),哪怕是服务中可以进行选择,也需要全部进行实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  class  CacheEmailNotifier  {    public  void  send (String msg) {         System.out.println("缓存数据" );         System.out.println("发送电子邮件" );     } } public  class  CacheWeChatNotifier  {    public  void  send (String msg) {         System.out.println("缓存数据" );         System.out.println("发送微信" );     } } ...... 
如果后续有调整,增加了新的记录方式或者通知方式,那么需要需要创建的类就更多了,并且可能还存在相同的逻辑被实现了多份不便于维护
问题出现的原因是因为将记录方式和通知方式这两种互不关联的模块在实现中耦合在一起 
实现 
通知方式 
定义接口 
1 2 3 public  interface  Sender  {    void  send (String msg) ;  } 
进行实现 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public  class  EmailSender  implements  Sender  {    @Override      public  void  send (final  String msg)  {         System.out.println("发送电子邮件:"  + msg);     } } public  class  WeChatSender  implements  Sender  {    @Override      public  void  send (final  String msg)  {         System.out.println("发送微信:"  + msg);     } } 
记录方式 
定义接口 
1 2 3 public  interface  Recorder  {    void  record (String msg) ; } 
进行实现 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public  class  CacheRecorder  implements  Recorder  {    @Override      public  void  record (final  String msg)  {         System.out.println("记录到缓存:"  + msg);     } } public  class  DbRecorder  implements  Recorder  {    @Override      public  void  record (final  String msg)  {         System.out.println("记录到数据库:"  + msg);     } } 
组织者 
抽象组织者 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public  class  AbstractNotifier  {    private  Recorder recorder;     private  Sender sender;     public  AbstractNotifier (final  Recorder recorder, final  Sender sender)  {         Assert.notNull(recorder);         Assert.notNull(sender);         this .recorder = recorder;         this .sender = sender;     }     public  void  handle (String msg)  {         recorder.record(msg);         sender.send(msg);     } } 
继承构造模块的实现 
1 2 3 4 5 6 7 public  class  CustomNotifier  extends  AbstractNotifier  {         public  CustomNotifier ()  {         super (new  DbRecorder (), new  EmailSender ());     }      } 
业务扩展 
需要增加新的发送方式,比如短信
1 2 3 4 5 6 public  class  ShortMessageSender  implements  Sender  {    @Override      public  void  send (final  String msg)  {         System.out.println("发送短信:"  + msg);     } } 
需要进行新的 notifier 的实现
1 2 3 4 5 6 7 public  class  PlusNotifier  extends  AbstractNotifier  {    public  PlusNotifier ()  {         super (new  CacheRecorder (), new  ShortMessageSender ());     } } 
实际应用 
JDBC
驱动为所有的关系型数据库提供一个通用的标准,这就是一个桥接模式的典型应用
进行连接 
先回顾一下 JDBC 的使用,使用 JDBC 连接 MySQL
数据库主要分为这样几步:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Class.forName("com.mysql.cj.jdbc.Driver" ); String  url  =  "jdbc:mysql://localhost:3306/test_db?" ;String  username  =  "root" ;String  password  =  "root" ;Connection  connection  =  DriverManager.getConnection(url, username, password);Statement  statement  =  connection.createStatement();String  query  =  "select * from test" ;ResultSet  resultSet  =  statement.executeQuery(query);connection.close(); 
Driver 
Class.forName("com.mysql.cj.jdbc.Driver"); 获取了 Driver
对象
1 2 3 4 5 6 7 8 9 10 11 12 13 public  class  Driver  extends  NonRegisteringDriver  implements  java .sql.Driver {    public  Driver ()  throws  SQLException {     }     static  {         try  {                          DriverManager.registerDriver(new  Driver ());         } catch  (SQLException var1) {             throw  new  RuntimeException ("Can't register driver!" );         }     } } 
通过调用 DriverManager 静态方法
registerDriver() 方法来将 MySQL 驱动注册到
DriverManager
DriverManager 
registerDriver() 方法具体如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public  static  synchronized  void  registerDriver (java.sql.Driver driver)     throws  SQLException { 	     registerDriver(driver, null ); } public  static  synchronized  void  registerDriver (java.sql.Driver driver,DriverAction da) throws  SQLException {         if (driver != null ) {         registeredDrivers.addIfAbsent(new  DriverInfo (driver, da));     } else  {                  throw  new  NullPointerException ();     }     println("registerDriver: "  + driver); } 
registeredDrivers 静态变量是一个 list
泛型 DriverInfo 是驱动信息的包装类
1 2 3 4 5 public  class  DriverManager  {         private  final  static  CopyOnWriteArrayList<DriverInfo> registeredDrivers = new  CopyOnWriteArrayList <>();      } 
DriverInfo 
DriverInfo 类中封装了 java.sql.Driver
接口,类似 Driver 信息管理者的角色
1 2 3 4 5 6 7 8 9 10 class  DriverInfo  {    final  Driver driver;     DriverAction da;     DriverInfo(Driver driver, DriverAction action) {         this .driver = driver;         da = action;     }      } 
Connection 
接下来进行连接的获取,Connection connection = DriverManager.getConnection(url, username, password);
Connection
接口是和特定数据库的连接会话,不同的数据库的连接会话都不相同 
1 2 3 4 5 public  interface  Connection   extends  Wrapper , AutoCloseable {    Statement createStatement ()  throws  SQLException;      } 
通过 DriverManager 中的 getConnection
方法,从 registeredDrivers
进行选择对应数据库驱动下的连接实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 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())); } private  static  Connection getConnection (     String url, java.util.Properties info, Class<?> caller)  throws  SQLException {         ClassLoader  callerCL  =  caller != null  ? caller.getClassLoader() : null ;     synchronized (DriverManager.class) {                  if  (callerCL == null ) {             callerCL = Thread.currentThread().getContextClassLoader();         }     }     if (url == null ) {         throw  new  SQLException ("The url cannot be null" , "08001" );     }     println("DriverManager.getConnection(\""  + url + "\")" );               SQLException  reason  =  null ;     for (DriverInfo aDriver : registeredDrivers) {                           if (isDriverAllowed(aDriver.driver, callerCL)) {             try  {                 println("    trying "  + aDriver.driver.getClass().getName());                 Connection  con  =  aDriver.driver.connect(url, info);                 if  (con != null ) {                                          println("getConnection returning "  + aDriver.driver.getClass().getName());                     return  (con);                 }             } catch  (SQLException ex) {                 if  (reason == null ) {                     reason = ex;                 }             }         } else  {             println("    skipping: "  + aDriver.getClass().getName());         }     }          if  (reason != null )    {         println("getConnection failed: "  + reason);         throw  reason;     }     println("getConnection: no suitable driver found for " + url);     throw  new  SQLException ("No suitable driver found for " + url, "08001" ); } 
Connection 实现 
在 Connection 接口的具体实现部分,MySQL
的连接是通过两层实现完成抽象部分的实现
不同的数据库会进行不同的实现
1 2 3 4 5 6 7 8 9 10 11 public  class  ConnectionImpl  implements  JdbcConnection , SessionEventListener, Serializable {    private  static  final  long  serialVersionUID  =  4009476458425101761L ;     private  static  final  SQLPermission  SET_NETWORK_TIMEOUT_PERM  =  new  SQLPermission ("setNetworkTimeout" );      } public  interface  JdbcConnection  extends  Connection , MysqlConnection, TransactionEventHandler {    JdbcPropertySet getPropertySet () ;     void  changeUser (String var1, String var2)  throws  SQLException;      } 
总结 
DriverManager 作为组织者,组织了
Driver(由管理者 DriverInfo 进行管理)和
Connection 两类模块
根据使用的数据库不同灵活地进行连接的选择
对应的类图:
总结 
当存在一段逻辑中分为多类模块时,可以考虑将各模块分别实现再进行组织降低耦合
优点
可以将庞大的类拆分成更有层次的结构,层次之间不存在耦合 
便于模块实现的扩展(开闭) 
切换不同的实现更加方便 
每个模块又可以定义出抽象部分和实现部分(单一职责) 
  
缺点
在已存在的功能上进行改造工程量较大(不同于适配器模式) 
高内聚的类进行桥接设计会使代码更加复杂 
  
 
参考 
桥接设计模式
(refactoringguru.cn) 
桥接模式的实际使用_最后一个NPE的博客-CSDN博客_桥接模式实战 
设计模式学习笔记(九)桥接模式及其应用
- 归斯君 - 博客园 (cnblogs.com)