zoukankan      html  css  js  c++  java
  • 简说设计模式——桥接模式

    一、什么是桥接模式

      桥接模式,又叫桥梁模式,顾名思义,就是有座“桥”,那这座桥是什么呢?就是一条聚合线(下方UML图),比如我们下面会举的例子,手机有手机品牌和手机游戏等等,每个手机品牌都有多款游戏,那是不是二者之间就是聚合关系了,这是合成/聚合复用原则的体现,当我们发现类有多层继承时就可以考虑使用桥接模式,用聚合代替继承。

           桥接模式(Bridge),将抽象部分与它的实现部分分离,使它们都可以独立地变化。UML结构图如下:

           其中,Abstraction为抽象化角色,定义出该角色的行为,同时保存一个对实现化角色的引用;Implementor是实现化角色,它是接口或者抽象类,定义角色必需的行为和属性;RefinedAbstraction为修正抽象化角色,引用实现化角色对抽象化角色进行修正;ConcreteImplementor为具体实现化角色,实现接口或抽象类定义的方法或属性。

           是不是感觉上面这段话很难懂,其实说简单点就是在Abstraction和Implementor之间架了一座桥(聚合线),这里体现了一个原则就是合成/聚合复用原则,具体看目录篇对基本原则的讲解及举例。下面放上模板代码。

        1. Abstraction抽象类

     1 public abstract class Abstraction {
     2 
     3     private Implementor imp;
     4 
     5     //约束子类必须实现该构造函数
     6     public Abstraction(Implementor imp) {
     7         this.imp = imp;
     8     }
     9     
    10     public Implementor getImp() {
    11         return imp;
    12     }
    13 
    14     //自身的行为和属性
    15     public void request() {
    16         this.imp.doSomething();
    17     }
    18     
    19 }

        2. Implementor抽象类

    1 public abstract class Implementor {
    2 
    3     public abstract void doSomething();
    4     public abstract void doAnything();
    5     
    6 }

        3. ConcreteImplementor

           这里可以编写多个具体实现类。

     1 public class ConcreteImplementorA extends Implementor {
     2 
     3     @Override
     4     public void doSomething() {
     5         System.out.println("具体实现A的doSomething执行");
     6     }
     7     
     8     @Override
     9     public void doAnything() {
    10         System.out.println("具体实现A的doAnything执行");
    11     }
    12 }

        4. RefinedAbstraction

     1 public class RefinedAbstraction extends Abstraction {
     2 
     3     //覆写构造函数
     4     public RefinedAbstraction(Implementor imp) {
     5         super(imp);
     6     }
     7     
     8     //修正父类行为
     9     @Override
    10     public void request() {
    11         super.request();
    12         super.getImp().doAnything();
    13     }
    14     
    15 }

        5. Client客户端

    1 public class Client {
    2 
    3     public static void main(String[] args) {
    4         Implementor imp = new ConcreteImplementorA();
    5         Abstraction abs = new RefinedAbstraction(imp);
    6         abs.request();
    7     }
    8     
    9 }

           运行结果如下:

    二、桥接模式的应用

        1. 何时使用

    • 系统可能有多个角度分类,每一种角度都可能变化时

        2. 方法

    • 把这种角度分类分离出来,让它们单独变化,减少它们之间的耦合(合成/聚合复用原则)

        3. 优点

    • 抽象和实现分离。桥梁模式完全是为了解决继承的缺点而提出的设计模式
    • 优秀的扩展能力
    • 实现细节对客户透明。客户不用关心细节的实现,它已经由抽象层通过聚合关系完成了封装

        4. 缺点

    • 会增加系统的理解与设计难度。由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程

        5. 使用场景

    • 不希望或不适用使用继承的场景
    • 接口或抽象类不稳定的场景
    • 重用性要求较高的场景

        6. 应用实例

    • 开关。我们可以看到的开关是抽象的,不用管里面具体怎么实现
    • 手机品牌与手机软件。两者间有一条聚合线,一个手机品牌可以有多个手机软件

        7. 注意事项

    • 不要一涉及继承就考虑该模式,尽可能把变化的因素封装到最细、最小的逻辑单元中,避免风险扩散
    • 当发现类的继承有n层时,可以考虑使用该模式

    三、桥接模式的实现

           下面我们举一个例子,就拿上面说的手机品牌与手机软件为例,我们可以让手机既可以按照手机品牌来分类,也可以按手机软件来分类。由于实现的方式有多种,桥接模式的核心意图就是把这些实现独立出来,让它们各自地变化,这就使得没中实现的变化不会影响其他实现,从而达到应对变化的目的。

           UML图如下:

        1. 手机品牌抽象类

           桥梁的一头。

     1 public abstract class HandsetBrand {
     2 
     3     protected HandsetSoft soft;
     4     
     5     //设置手机软件
     6     public void setHandsetSoft(HandsetSoft soft) {
     7         this.soft = soft;
     8     }
     9     
    10     //运行
    11     public abstract void run();
    12     
    13 }

        2. 手机软件抽象类

           桥梁的另一头。两者通过一条聚合线连接,表示一个手机品牌可以有多个软件。

    1 public abstract class HandsetSoft {
    2 
    3     public abstract void run();
    4     
    5 }

        3. 各类手机品牌

           这里写一个,多余的不再赘述。

    1 public class HandsetBrandA extends HandsetBrand {
    2 
    3     @Override
    4     public void run() {
    5         soft.run();
    6     }
    7     
    8 }

        4. 各类手机软件

           有游戏、通讯录等等,这里写一个,多余不再赘述。

    1 public class HandsetGame extends HandsetSoft {
    2 
    3     @Override
    4     public void run() {
    5         System.out.println("运行手机游戏");
    6     }
    7 
    8 }

        5. Client客户端

     1 public class Client {
     2 
     3     public static void main(String[] args) {
     4         HandsetBrand ab;
     5         
     6         //使用A品牌手机
     7         ab = new HandsetBrandA();
     8         System.out.println("A品牌手机:");
     9         
    10         ab.setHandsetSoft(new HandsetGame());
    11         ab.run();
    12         
    13         ab.setHandsetSoft(new HandsetAddressList());
    14         ab.run();
    15         
    16         //分隔符
    17         System.out.println("---------------");
    18         
    19         //使用B品牌手机
    20         ab = new HandsetBrandB();
    21         System.out.println("B品牌手机:");
    22         
    23         ab.setHandsetSoft(new HandsetGame());
    24         ab.run();
    25         
    26         ab.setHandsetSoft(new HandsetAddressList());
    27         ab.run();
    28     }
    29     
    30 }

           运行结果如下:

           这样我现在如果想要增加一个功能,比如音乐播放器,那么只有增加这个类就可以了,不会影响到其他任何类,类的个数增加也只是一个;如果是要增加S品牌,只需要增加一个品牌的子类就可以了,个数也是一个,不会影响到其他类。这显然符合开放-封闭原则。

           而这里用到的合成/聚合复用原则是一个很有用处的原则,即优先使用对象的合成或聚合,而不是继承。究其原因是因为继承是一种强耦合的结构,父类变,子类就必须变。

           源码地址:https://gitee.com/adamjiangwh/GoF

  • 相关阅读:
    redis 安装配置
    ORM 效率补充
    function*
    路由器
    nodejs开发辅助工具nodemon
    npm淘宝镜像配置
    Node中的模块系统
    artTemplate不仅可以在浏览器中使用,还可以在node中使用
    代码风格JavaScript standard style与Airbnb style
    osChina.net工具
  • 原文地址:https://www.cnblogs.com/adamjwh/p/9033548.html
Copyright © 2011-2022 走看看