结构型模式 - 桥接模式

系列文章目录

设计模式 - 设计原则

创建型模式 - 单例模式(一)
创建型模式 - 工厂模式(二)
创建型模式 - 原型模式(三)
创建型模式 - 建造者模式(四)

结构型模式 - 适配器模式(一)
结构型模式 - 桥接模式(二)
结构型模式 - 装饰器模式(三)
结构型模式 - 组合模式(四)
结构型模式 - 外观模式(五)
结构型模式 - 享元模式(六)
结构型模式 - 代理模式(七)

行为型模式 - 模板方法模式(一)
行为型模式 - 命令模式(二)
行为型模式 - 访问者模式(三)
行为型模式 - 迭代器模式(四)
行为型模式 - 观察者模式(五)
行为型模式 - 中介者模式(六)
行为型模式 - 备忘录模式(七)
行为型模式 - 解释器模式(八)
行为型模式 - 状态模式(九)
行为型模式 - 策略模式(十)
行为型模式 - 责任链模式(十一)



前言

代码地址


一、桥接模式

1.1 桥接模式介绍

  • 桥接(Bridge)模式:
  • 是将抽象与实现放在两个不同的类层次中,是两个层次可以独立改变;
  • 桥接模式基于类的最小设计原则,通过封装、聚合及继承等行为让不同的类承担不同的职责,他的主要特点是吧抽象(Abstraction)与行为实现(Implementation)分离开,从而可以保持各部分的独立性以及应对他们的功能扩展;
  • 桥接模式的思想在于:在系统设计期间,如果一个类里面的一些东西,会扩展很多,这个东西应该分离出来

1.2 桥接模式结构

在这里插入图片描述

  • 抽象化(Abstraction)角色:
  • 定义抽象类,并包含一个对实现化对象的引用;
  • 扩展抽象化(Refined Abstraction)角色:
  • 是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法;
  • 实现化(Implementor)角色:
  • 定义实现化角色的接口,供扩展抽象化角色使用;
  • 具体实现化(Concrete Implementor)角色:
  • 给出实现化角色接口的具体实现;

二、实现

例子:

  • 在现实生活中,某些类具有两个或多个维度的变化,如:手机可以按照商品拆分又可按渠道拆分。如何设计能实现不同商品和不同渠道的手机呢?如果使用继承方式,m种功能和n中渠道的手机就有m*n种,不但对应的子类很多,而且扩展困难,怎么解决呢?

在这里插入图片描述

2.1 普通实现

在这里插入图片描述

  • 采用多层继承结构的形式,类的个数巨大;扩展时,个数又会爆发式增加,随之带来的就是维护、使用、运行等成本的增加;
  • 继承从一开始就把抽象角色和实现角色绑定,是一种强关联关系,并不符合组合复用原则;
  • 继承将父类暴露给子类,如果父类变化,子类势必将会受到影响,将不得不做出改变,不符合开闭原则;
  • 对于每一个实现类,比如上面的例子:实现类可能同时商品和渠道里面的东西,涉及到任何一方的改变都会其他修改,不符合单一职责;
  • 仔细观察可以发现,之所以类会如此膨胀,扩展如此困难的原因在于它不止一个维度,是否可以将这两个维度进行分类?

    • 如果他们能够分离,也就意味着不会完全使用继承层次结构,因为继承是强关联,完全继承,就不会分离,那么,就要考虑类的组合了;
    • 如果可以分离,将他们拆分为不同的维度,那么就不会出现类的爆炸式增长了,因为一旦分离,就可以独立发展;
    • 如果可以分离,那么他们各自负责不同的维度,职责将会更加单一;
  • 所以解决方案是:面向商品(本例中)编程,商品不关注渠道的细节,将与渠道相关的实现剥离开来,而渠道的实现部分,通过组合的方式,组合到商品抽象中;

2.2 桥接实现

在这里插入图片描述

package com.dozezz.designpattern.bridge.bridge;

/**
 * @Description: 抽象手机类,手机有各种销售渠道且价格不一样
 * @Author: dozezz
 * @Date: 2021/7/17 19:15
 * @Version: 1.0
 */
public abstract class AbstractPhone {

    /**
     * 分离渠道(桥接的關鍵點)
     * 設計期間就要想好的,適配器是一開始沒有,功能迭代時候需要的時候再加上的;
     */
    AbstractSale abstractSale;

    /**
     * 如果这么些的话,需要多个实现;
     * @return
     */
//    abstract String getPrice();


    abstract String getPhone();

    public void setAbstractSale(AbstractSale abstractSale) {
        this.abstractSale = abstractSale;
    }
}
package com.dozezz.designpattern.bridge.bridge;

/**
 * @Description: 抽象銷售渠道類
 * @Author: dozezz
 * @Date: 2021/7/17 19:27
 * @Version: 1.0
 */
public abstract class AbstractSale {
    private String type;
    private String price;

    public AbstractSale(String type, String price){
        this.type = type;
        this.price = price;
    }

    String getSale(){
        return String.format("渠道: %s, 價格: %s", type, price);
    }

}
package com.dozezz.designpattern.bridge.bridge;

/**
 * @Description: 商品(性能手機)
 * @Author: dozezz
 * @Date: 2021/7/17 19:47
 * @Version: 1.0
 */
public class HuaweiPhone extends AbstractPhone{
    @Override
    String getPhone() {
        return String.format("HUAWEI Mate X2: %s", abstractSale.getSale());
    }
}
package com.dozezz.designpattern.bridge.bridge;

/**
 * @Description: 商品(拍照手機)
 * @Author: dozezz
 * @Date: 2021/7/17 19:48
 * @Version: 1.0
 */
public class XiaomiPhone extends AbstractPhone{
    @Override
    String getPhone() {

        return String.format("Redmi Note 10 Pro: %s",abstractSale.getSale());
    }
}
package com.dozezz.designpattern.bridge.bridge;

/**
 * @Description: 線下渠道
 * @Author: dozezz
 * @Date: 2021/7/17 19:45
 * @Version: 1.0
 */
public class OfflineSale extends AbstractSale{
    public OfflineSale(String type, String price) {
        super(type, price);
    }
}
package com.dozezz.designpattern.bridge.bridge;

/**
 * @Description: 線上渠道
 * @Author: dozezz
 * @Date: 2021/7/17 19:46
 * @Version: 1.0
 */
public class OnlineSale extends AbstractSale{
    public OnlineSale(String type, String price) {
        super(type, price);
    }
}
package com.dozezz.designpattern.bridge.bridge;

/**
 * @Description: 測試類
 * @Author: dozezz
 * @Date: 2021/7/17 19:50
 * @Version: 1.0
 */
public class ClientTest {
    public static void main(String[] args) {
        HuaweiPhone huaweiPhone = new HuaweiPhone();

        huaweiPhone.setAbstractSale(new OnlineSale("京東線上渠道", "17799 元"));
        System.out.println(huaweiPhone.getPhone());

        huaweiPhone.setAbstractSale(new OnlineSale("北京門店渠道", "17799 元"));
        System.out.println(huaweiPhone.getPhone());

    }
}

三、桥接模式总结

3.1 桥接模式适用场景

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

3.2 桥接模式与适配器模式区别

  • 适配器模式的主要目的是让因为接口不兼容而不能相互工作的类能够正常工作,换句话来说就是他们本身不同,我用“纽带”Adapter将他们连接起来,比如:张三和李四不认识,王五介绍他们相互认识;
  • 桥接模式是将原本或许紧密结合在一起的抽象与实现,进行分离,使他们能够各自独立的发展,是把连接在一起的两个事物,拆分开来,然后用“纽带”“桥梁”(也就是对象的引用)将他们连接起来,比如:张三和李四整天黏在一起干活干得不好太乱,王五说你们以后各干各的,我作为你们的接口人;

3.3 桥接模式与装饰器模式区别

  • 装饰器模式中,使用组合而不是继承对类的功能进行扩展,避免了类的个数的爆炸增长,与桥梁模式的结果相同,都解决了类爆炸增长的问题,都避免了过多没用的子类;
  • 装饰器模式侧重于功能的动态增加,将额外的功能提取到子类中,更关注于功能的动态扩展组合
  • 桥接模式是将原本系统中的实现细节抽取出来,比如原本抽象概念与实例化全部都是一个类层次结构中,把所有实现细节,比如示例中的渠道相关实现,抽取出来,进行分离,达到抽象与实现分离的目的,更关注与抽象与实现的分离,获得更多的灵活性

3.4 桥接模式优缺点

  • 抽象与实现分离,实现细节对客户透明,扩展性强;
  • 符合开闭原则、合成复用原则;
  • 由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个独立变化的维度,这增加了系统的理解和设计难度;

三、参考文献

  • http://c.biancheng.net/view/1354.html
  • https://www.bilibili.com/video/BV1G4411c7N4?p=54&spm_id_from=pageDriver
  • https://baiyp.ren/JAVA%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-04%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值