jjzjj

设计模式之桥接模式

pluto_charon 2023-03-28 原文

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

抽象化

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

实现化

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

脱耦

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

手机已经是我们日常生活不可缺少的一环了,国内的手机有华为,小米,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. 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。

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

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

有关设计模式之桥接模式的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  4. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  5. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  6. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  7. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  8. ruby-on-rails - environment.rb 中设置的常量在开发模式中消失 - 2

    了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl

  9. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

  10. Ruby:标准递归模式 - 2

    我经常迷上ruby​​的一件事是递归模式。例如,假设我有一个数组,它可能包含无限深度的数组作为元素。所以,例如:my_array=[1,[2,3,[4,5,[6,7]]]]我想创建一个方法,可以将数组展平为[1,2,3,4,5,6,7]。我知道.flatten可以完成这项工作,但这个问题是作为我经常遇到的递归问题的一个例子-因此我试图找到一个更可重用的解决方案。简而言之-我猜这种事情有一个标准模式,但我想不出任何特别优雅的东西。任何想法表示赞赏 最佳答案 递归是一种方法,它不依赖于语言。您在编写算法时要考虑两种情况:再次调用函数的情

随机推荐