文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

设计模式系列—桥接模式

2024-12-14 04:55

关注

本篇和大家一起来学习桥接模式相关内容。

 模式定义
将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。

模式实现如下:

  1. package com.niuh.designpattern.bridge.v1; 
  2.  
  3.  
  4. public class BridgePattern { 
  5.     public static void main(String[] args) { 
  6.  
  7.         Implementor imple=new ConcreteImplementorA(); 
  8.         Abstraction abs=new RefinedAbstraction(imple); 
  9.         abs.Operation(); 
  10.  
  11.     } 
  12.  
  13. //实现化角色 
  14. interface Implementor { 
  15.     void OperationImpl(); 
  16.  
  17. //具体实现化角色 
  18. class ConcreteImplementorA implements Implementor { 
  19.  
  20.     public void OperationImpl() { 
  21.         System.out.println("具体实现化(Concrete Implementor)角色被访问"); 
  22.     } 
  23.  
  24. //抽象化角色 
  25. abstract class Abstraction { 
  26.     protected Implementor imple; 
  27.  
  28.     protected Abstraction(Implementor imple) { 
  29.         this.imple = imple; 
  30.     } 
  31.  
  32.     public abstract void Operation(); 
  33.  
  34. //扩展抽象化角色 
  35. class RefinedAbstraction extends Abstraction { 
  36.     protected RefinedAbstraction(Implementor imple) { 
  37.         super(imple); 
  38.     } 
  39.  
  40.     public void Operation() { 
  41.         System.out.println("扩展抽象化(Refined Abstraction)角色被访问"); 
  42.         imple.OperationImpl(); 
  43.     } 

输出结果如下:

  1. 扩展抽象化(Refined Abstraction)角色被访问 
  2. 具体实现化(Concrete Implementor)角色被访问 

解决的问题
在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。

模式组成
可以将抽象化部分与实现化部分分开,取消二者的继承关系,改用组合关系。

实例说明
实例概况
某公司开发了一个财务管理系统,其中有个报表生成器的工具模块,客户可以指定任意一种报表类型,如基本报表,往来报表,资金报表,资产报表等,并且可以指定不同 的报表样式,如饼图,柱状图等。系统设计人员针对这个报表生成器的结构设计了如下图所示的类图。

后来在客户使用过程中,客户又希望增加一个新的报表和新的线形图,开发人员这个时候发现维护起来非常麻烦,设计人员经过仔细分析,发现存在严重的问题,因为新增加一个报表或者图,需要增加很多子类。所以,系统分析师最终对这个模块根据面向对象的设计原则对上面的方案进行了重构,重构后的图如下所示。

在本重构方案中,将报表和图形设计成两个继承结构,两者都可以独立变化,编程的时候可以只针对抽象类编码,而在运行的时候再将具体的图形子类对象注入到具体的 报表类中。这样的话,系统就具有良好的可扩展性和可维护性,并且满足了面向对象设计原则的开闭原则。

使用步骤
步骤1:定义实现化角色,报表接口

  1. interface IReport { 
  2.     void operationImpl(); 

步骤2:定义具体实现化角色(基本报表、往来报表、资金报表)

  1. class BasicReport implements IReport { 
  2.  
  3.     @Override 
  4.     public void operationImpl() { 
  5.         System.out.println("基本报表被访问."); 
  6.     } 
  7.  
  8. class IntercourseReport implements IReport { 
  9.  
  10.     @Override 
  11.     public void operationImpl() { 
  12.         System.out.println("往来报表被访问."); 
  13.     } 
  14.  
  15. class CapitalReport implements IReport { 
  16.  
  17.     @Override 
  18.     public void operationImpl() { 
  19.         System.out.println("资金报表被访问."); 
  20.     } 

步骤3:定义抽象化角色,图形

  1. abstract class AbstractionGraph { 
  2.     protected IReport iReport; 
  3.  
  4.     public AbstractionGraph(IReport iReport) { 
  5.         this.iReport = iReport; 
  6.     } 
  7.  
  8.     abstract void operation(); 

步骤4:定义扩展抽象化角色(柱状图、饼图)

  1. class Barchart extends AbstractionGraph { 
  2.  
  3.     public Barchart(IReport iReport) { 
  4.         super(iReport); 
  5.     } 
  6.  
  7.     @Override 
  8.     void operation() { 
  9.         System.out.println("柱状图被访问."); 
  10.         iReport.operationImpl(); 
  11.     } 
  12.  
  13. class Piechart extends AbstractionGraph { 
  14.  
  15.     public Piechart(IReport iReport) { 
  16.         super(iReport); 
  17.     } 
  18.  
  19.     @Override 
  20.     void operation() { 
  21.         System.out.println("饼图被访问."); 
  22.         iReport.operationImpl(); 
  23.     } 

步骤5:测试

  1. public class BridgePattern { 
  2.  
  3.     public static void main(String[] args) { 
  4.         //实现化和抽象化分离 
  5.  
  6.         // 基本报表 
  7.         IReport basicReport = new BasicReport(); 
  8.         // 往来报表 
  9.         IReport intercourseReport = new IntercourseReport(); 
  10.         // 资金报表 
  11.         IReport capitalReport = new CapitalReport(); 
  12.  
  13.         // 基本报表使用柱状图 
  14.         AbstractionGraph barchart = new Barchart(basicReport); 
  15.         barchart.operation(); 
  16.  
  17.         // 基本报表使用饼图 
  18.         AbstractionGraph piechart = new Piechart(basicReport); 
  19.         piechart.operation(); 
  20.     } 
  21.  

输出结果

优点
桥接模式遵循了里氏替换原则和依赖倒置原则,最终实现了开闭原则,对修改关闭,对扩展开放。这里将桥接模式的优缺点总结如下。

桥接(Bridge)模式的优点:

缺点
由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个独立变化的维度,这增加了系统的理解与设计难度。

应用场景
当一个类内部具备两种或多种变化维度时,使用桥接模式可以解耦这些变化的维度,使高层代码架构稳定。

桥接模式通常适用于以下场景:

  1. 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时;
  2. 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时;
  3. 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。

桥接模式的一个常见使用场景就是替换继承。我们知道,继承拥有很多优点,比如,抽象、封装、多态等,父类封装共性,子类实现特性。继承可以很好的实现代码复用(封装)的功能,但这也是继承的一大缺点。

因为父类拥有的方法,子类也会继承得到,无论子类需不需要,这说明继承具备强侵入性(父类代码侵入子类),同时会导致子类臃肿。因此,在设计模式中,有一个原则为优先使用组合/聚合,而不是继承。

桥接模式模式的扩展
在软件开发中,有时桥接(Bridge)模式可与适配器模式联合使用。当桥接(Bridge)模式的实现化角色的接口与现有类的接口不一致时,可以在二者中间定义一个适配器将二者连接起来,其结构图如下:

源码中的应用

  1. JDBC驱动程序 
  2. ...... 

DriverManager类

DriverManager作为一个抽象化角色,聚合了实现化角色Connection,只不过与标准的桥梁模式不一样的是,DriverManager类下面没有子类。

  1. //  Worker method called by the public getConnection() methods. 
  2. private static Connection getConnection( 
  3.  String url, java.util.Properties info, Class caller) throws SQLException { 
  4.          
  5.         ClassLoader callerCL = caller != null ? caller.getClassLoader() : null
  6.         synchronized(DriverManager.class) { 
  7.             // synchronize loading of the correct classloader. 
  8.             if (callerCL == null) { 
  9.                 callerCL = Thread.currentThread().getContextClassLoader(); 
  10.             } 
  11.         } 
  12.  
  13.         if(url == null) { 
  14.             throw new SQLException("The url cannot be null""08001"); 
  15.         } 
  16.  
  17.         println("DriverManager.getConnection(\"" + url + "\")"); 
  18.  
  19.         // Walk through the loaded registeredDrivers attempting to make a connection
  20.         // Remember the first exception that gets raised so we can reraise it. 
  21.         SQLException reason = null
  22.  
  23.         for(DriverInfo aDriver : registeredDrivers) { 
  24.             // If the caller does not have permission to load the driver then 
  25.             // skip it. 
  26.             if(isDriverAllowed(aDriver.driver, callerCL)) { 
  27.                 try { 
  28.                     println("    trying " + aDriver.driver.getClass().getName()); 
  29.                     Connection con = aDriver.driver.connect(url, info); 
  30.                     if (con != null) { 
  31.                         // Success! 
  32.                         println("getConnection returning " + aDriver.driver.getClass().getName()); 
  33.                         return (con); 
  34.                     } 
  35.                 } catch (SQLException ex) { 
  36.                     if (reason == null) { 
  37.                         reason = ex; 
  38.                     } 
  39.                 } 
  40.  
  41.             } else { 
  42.                 println("    skipping: " + aDriver.getClass().getName()); 
  43.             } 
  44.  
  45.         } 
  46.  
  47.         // if we got here nobody could connect
  48.         if (reason != null)    { 
  49.             println("getConnection failed: " + reason); 
  50.             throw reason; 
  51.         } 
  52.  
  53.         println("getConnection: no suitable driver found for "+ url); 
  54.         throw new SQLException("No suitable driver found for "+ url, "08001"); 

PS:以上代码提交在 Github :

https://github.com/Niuh-Study/niuh-designpatterns.git

来源:今日头条内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯