设计模式之桥接模式

桥接模式又称桥梁模式,属于结构型模式,是指将抽象化实现化 脱耦,使得二者可以独立的变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。

抽象化

存在于多个实体中的共同的概念性联系,就是抽象化,作为一个过程,抽象化就是忽略一些信息,从而把不同的实体当作同样的实体对待。通常情况下,一组对象如果具有相同的概念性联系,那么他们就可以通过一个共同的类来描述,如果一些类具有相同的概念性联系,往往可以通过一个共同的抽象类来描述,在更加复杂的情况下,可以使用一个继承关系的包括抽象类和具体子类的等级结构来描述。

实现化

抽象化给出的具体实现,就是实现化。一个类的实例就是这个类的实现化,一个具体子类是它的抽象超类的实现化。在更加复杂的情况下,实现化也可以是与抽象化等级结构相平行的等级结构,同样可以由抽象类和具体类组成。

脱耦

所谓耦合,就是两个实体的行为的某种强关联,而将他们的强关联去掉,就是脱耦。在这里脱耦是指将抽象化和实现化之间的耦合解脱开,或者是将他们之间的强关联改换成弱关联。

手机已经是我们日常生活不可缺少的一环了,国内的手机有华为,小米,VIVO等几个厂商,每个厂商旗下又有好多款不同配置的手机,接下来就以手机打电话的功能作为例子讲解。

传统方式实现打电话功能:

传统模式如果要再加一个手机样式(旋转式),就需要对各个品牌添加call()的功能,这样增加了代码的维护成本。

桥接模式

对于传统模式出现的问题,使用桥接模式就可以很好的解决。

桥接模式的UML类图如下:

从UML类图可以看出,这个系统含有两个等级结构:

  • 有抽象化角色和扩展抽象化角色组成的抽象化等级结构
  • 有实现化角色和具体实现化角色所组成的实现化等级结构

桥接模式所涉及到抽象化角色,扩展抽象化角色,实现化角色,具体实现化角色等几种角色:

  • 抽象化角色:给出定义,并保存一个对实现化对象的引用
  • 扩展抽象化角色:扩展抽象化角色、改变和扩展父类对抽象化的定义
  • 实现化角色:这个角色给出实现化角色的接口,但不给出具体的实现,必须指出的是,这个接口不一定和抽象化角色的接口定义相同。实际上,这两个接口可以非常不一样,实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作
  • 具体实现化角色:这个角色给出实现化角色接口的具体实现

例子的UML类图:

抽象化角色:

package com.charon.bridge;

/**
 * @className: Brand
 * @description: 抽象化产品角色
 * @author: charon
 * @create: 2022-03-18 22:51
 */
public interface Brand {
    void open();

    void close();

    void call();
}

扩展抽象化角色:

package com.charon.bridge;

/**
 * @className: Vivo
 * @description:
 * @author: charon
 * @create: 2022-03-18 22:55
 */
public class Vivo implements Brand {
    @Override
    public void open() {
        System.out.println("vivo手机开机了。。。。");
    }

    @Override
    public void close() {
        System.out.println("vivo手机关机了。。。。");
    }

    @Override
    public void call() {
        System.out.println("vivo手机打电话。。。。");
    }
}

package com.charon.bridge;

/**
 * @className: XiaoMi
 * @description:
 * @author: charon
 * @create: 2022-03-18 22:56
 */
public class XiaoMi implements Brand{
    @Override
    public void open() {
        System.out.println("小米手机开机了。。。。");
    }

    @Override
    public void close() {
        System.out.println("小米手机关机了。。。。");
    }

    @Override
    public void call() {
        System.out.println("小米手机打电话。。。。");
    }
}

实现化角色:

package com.charon.bridge;

/**
 * @className: Phone
 * @description: 实现化角色
 * @author: charon
 * @create: 2022-03-18 22:52
 */
public abstract class Phone {

    private Brand brand;

    public Phone(Brand brand) {
        this.brand = brand;
    }

    protected void open(){
      this.brand.open();
    }

    protected void call(){
        this.brand.call();
    }

    protected void close(){
        this.brand.close();
    }
}

具体实现化角色:

package com.charon.bridge;

/**
 * @className: FoldedPhone
 * @description:
 * @author: charon
 * @create: 2022-03-18 22:57
 */
public class FoldedPhone extends Phone{
    public FoldedPhone(Brand brand) {
        super(brand);
    }

    @Override
    protected void open(){
        super.open();
        System.out.println("折叠样式手机开机。。。。");
    }

    @Override
    protected void call(){
        super.call();
        System.out.println("折叠样式手机打电话。。。。");
    }

    @Override
    protected void close(){
        super.close();
        System.out.println("折叠样式手机关机。。。。");
    }
}

package com.charon.bridge;

/**
 * @className: UpRightPhone
 * @description:
 * @author: charon
 * @create: 2022-03-18 22:59
 */
public class UpRightPhone extends Phone{

    public UpRightPhone(Brand brand) {
        super(brand);
    }

    @Override
    protected void open(){
        super.open();
        System.out.println("直立样式手机开机。。。。");
    }

    @Override
    protected void call(){
        super.call();
        System.out.println("直立样式手机打电话。。。。");
    }

    @Override
    protected void close(){
        super.close();
        System.out.println("直立样式手机关机。。。。");
    }
}

测试:

package com.charon.bridge;

/**
 * @className: Client
 * @description:
 * @author: charon
 * @create: 2022-03-18 22:51
 */
public class Client {
    public static void main(String[] args) {
        // 获取折叠样式手机(品牌+样式)
        FoldedPhone xiaomiFoldedPhone = new FoldedPhone(new XiaoMi());
        xiaomiFoldedPhone.open();
        xiaomiFoldedPhone.call();
        xiaomiFoldedPhone.close();

        UpRightPhone xiaomiUpRightPhone = new UpRightPhone(new XiaoMi());
        xiaomiUpRightPhone.open();
        xiaomiUpRightPhone.call();
        xiaomiUpRightPhone.close();
    }
}

如此这般,不管是新增一个产品还是新增一个样式,代码的改动都非常小。

桥接模式遵循了里氏替换原则和依赖倒置原则,最终实现了开闭原则。

桥接模式的优点:

  1. 抽象与实现分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统
  2. 对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其他的部分有具体业务来完成
  3. 桥接模式提代了多层继承方案,可以减少子类的个数,降低系统的管理和维护成本

桥接模式的缺点:

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

桥接模式的应用场景

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

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

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

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

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

posted @ 2022-03-18 23:24  pluto_charon  阅读(1729)  评论(1编辑  收藏  举报