结构型设计模式06-桥接模式

news2024/11/23 12:04:39

🧑‍💻作者:猫十二懿

❤️‍🔥账号:CSDN 、掘金 、个人博客 、Github

🎉公众号:猫十二懿

桥接模式

1、桥接模式模式介绍

桥接模式(Bridge Pattern)是一种结构型模式之一。它 通过将抽象部分和实现部分分离,使它们可以独立地变化,从而实现了解耦合的设计桥接模式使用组合而不是继承的方式来连接抽象和实现,使得两者可以独立地变化,互不影响

1.1 桥接模式基本实现

在桥接模式中,抽象部分和实现部分分别定义为两个独立的接口(Abstraction 和 Implementor)。抽象部分维护一个指向实现部分的引用,它自身包含了一些基本操作,而这些操作的具体实现则委托给实现部分。这样,抽象部分可以通过调用实现部分的方法来完成具体的功能。

image-20230517120253245

通过桥接模式,可以将一个系统分为多个独立的维度,并且可以独立地对每个维度进行扩展和修改。它提供了更好的灵活性和可扩展性,同时也符合面向对象设计的原则。

桥接模式的主要参与角色:

  • Abstraction(抽象部分):定义抽象部分的接口,维护一个指向实现部分的引用。

    /**
     * @author Shier
     * CreateTime 2023/5/17 12:08
     */
    public abstract class Abstraction {
        // 聚合 Implementor 
        protected Implementor implementor;
    
        public void setImplementor(Implementor implementor) {
            this.implementor = implementor;
        }
    
        public abstract void operation();
    }
    
  • RefinedAbstraction(扩充抽象部分):对抽象部分进行扩展,增加新的功能。

    /**
     * @author Shier
     * CreateTime 2023/5/17 12:09
     * 扩充抽象
     */
    public class RefinedAbstraction extends Abstraction {
        @Override
        public void operation() {
            System.out.println("具体的Abstraction执行了");
            implementor.operation();
        }
    }
    
  • Implementor(实现部分):定义实现部分的接口,供抽象部分调用。

    /**
     * @author Shier
     * CreateTime 2023/5/17 12:05
     * 实现
     */
    public abstract class Implementor {
        public abstract void  operation();
    }
    
  • ConcreteImplementor(具体实现部分):实现实现部分的接口,具体完成具体的功能。

    /**
     * @author Shier
     * CreateTime 2023/5/17 12:06
     * 具体实现A
     */
    public class ConcreteImplementorA extends Implementor {
        @Override
        public void operation() {
            System.out.println("具体实现 A 的方法执行了!");
        }
    }
    
    
    /**
     * @author Shier
     * CreateTime 2023/5/17 12:06
     * 具体实现B
     */
    public class ConcreteImplementorB extends Implementor {
        @Override
        public void operation() {
            System.out.println("具体实现 B 的方法执行了!");
        }
    }
    

客户端:

/**
 * @author Shier
 * CreateTime 2023/5/17 12:09
 */
public class BridgeClient {
    public static void main(String[] args) {
        Abstraction abstraction= new RefinedAbstraction();
        // 传入A实现类
        abstraction.setImplementor(new ConcreteImplementorA());
        abstraction.operation();

        // 传入B实现类
        abstraction.setImplementor(new ConcreteImplementorB());
        abstraction.operation();
    }
}

结果如下:

image-20230517121837060

桥接模式可以应用于很多场景,特别是当一个系统需要在多个维度上进行扩展和变化时。例如,当一个抽象类有多个子类,而这些子类又有多个实现类时,可以使用桥接模式将抽象类与实现类解耦,使得它们可以独立地变化和扩展。

桥接模式实现系统可能有多角度分类,每一种分类都有可能变化, 那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。

2、具体案例说明

案例背景:在手机问世之初,存在很多的限制,两个不同品牌手机不能使用同一个应用,也就是存在兼容的问题,很多APP都是各自生产的,只能兼容自家的手机硬件。那么两个品牌手机,都有游戏,我觉得从面向对象的思想来说,应该有一个父类 ‘手机品牌游戏’,然后让N和M品牌的手机游戏都继承于它,这样可以实现同样的运行方法。

同时,由于手机都需要通讯录功能,于是N品牌和M品牌都增加了通讯录的增删改查功能。

2.1 不使用桥接模式实现

会出现紧耦合的问题

先看看代码结构图:

image-20230517122907626

父类应该是 ‘手机品牌’ ,下有 ‘手机品牌M’ 和 ‘手机品牌N’,每个子类下各有 ‘通讯录’ 和 ‘游戏’ 子类。

手机类:

/**
 * @author Shier
 * CreateTime 2023/5/17 16:54
 * 手机品牌
 */
public class PhoneBrand {
    public void run(){}
}

手机品牌N和手机品牌M类:

/**
 * @author Shier
 * CreateTime 2023/5/17 16:55
 * 手机品牌M
 */
public class PhoneBrandM extends PhoneBrand{
}

/**
 * @author Shier
 * CreateTime 2023/5/17 16:55
 * 手机品牌N
 */
public class PhoneBrandN extends PhoneBrand{
}

下属的各自通讯录类和游戏类:

/**
 * @author Shier
 * CreateTime 2023/5/17 16:56
 * 游戏
 */
public class PhoneBrandMGame extends PhoneBrandM {
    public void run(){
        System.out.println("执行M品牌手机游戏");
    }
}

public class PhoneBrandNGame extends PhoneBrandN {
    public void run(){
        System.out.println("执行N品牌手机游戏");
    }
}
/**
 * @author Shier
 * CreateTime 2023/5/17 16:57
 * 通讯录
 */
public class PhoneBrandMAddressList extends PhoneBrandM{
    public void run(){
        System.out.println("运行M品牌手机通讯录");
    }
}

public class PhoneBrandNAddressList extends PhoneBrandN {
    public void run() {
        System.out.println("运行N品牌手机通讯录");
    }
}

客户端代码:

/**
 * @author Shier
 * CreateTime 2023/5/17 16:59
 */
public class PhoneClient {
    public static void main(String[] args) {
        // 调用M手机游戏
        PhoneBrand mGame = new PhoneBrandMGame();
        mGame.run();
        // 调用M手机通讯录
        PhoneBrand  mAddressList= new PhoneBrandMAddressList();
        mAddressList.run();

        // 调用N手机游戏
        PhoneBrand nGame = new PhoneBrandNGame();
        nGame.run();
        // 调用N手机通讯录
        PhoneBrand  nAddressList= new PhoneBrandNAddressList();
        nAddressList.run();

    }
}

image-20230517170428103

功能来说,算是实现了,但是要是再增加一个音乐播放呢?

在每个品牌的下面都增加一个子类。但是这些子类的却别并不是很大。

如果又有了新的手机品牌,它同样有以上的功能,就得再增加这些重复的子类。

我们一直在用面向对象的理论设计的,先有一个品牌,然后多个品牌就抽象出一个品牌抽象类,对于每个功能,就都继承各自的品牌。或者,不从品牌,从手机软件的角度去分类,这有什么问题呢?

是呀,就像我们刚开始学会用面向对象的继承时,感觉它既新颖又功能强大,所以只要可以用,就都用上继承。这就好比是有了新锤子,所有的东西看上去都成了钉子。但事实上,很多情况用继承会带来麻烦。类之间的耦合度很高,修改一个类牵涉到其他的类

比如,对象的继承关系是在编译时就定义好了,所以无法在运行时改变从父类继承的 实现。子类的实现与它的父类有非常紧密的依赖关系,以至于父类实现中的 任何变化必然会导致子类发生变化。当你需要复用子类时,如果继承下来的 实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种 依赖关系限制了灵活性并最终限制了复用性。

这样的继承结构,如果不断地增加新品牌或新功能,类会越来越多的。

这里就要使用到 合成 / 聚合复用原则 :尽量使用合成/聚合,尽量不要使用 类继承。

2.2 使用桥接模式实现

使用的 合成 / 聚合复用模式 的结构图:

image-20230517171235082

手机品牌包含手机软件,但软件并不是品牌的一部分,所以它们之间是聚合关系。

手机软件抽象类:

/**
 * @author Shier
 * CreateTime 2023/5/17 17:14
 * 手机软件类
 */
public abstract class PhoneSoft {
    // 执行
    public abstract void run();
}

游戏、通讯录等具体类:

/**
 * @author Shier
 * CreateTime 2023/5/17 17:15
 * 手机游戏
 */
public class PhoneGame extends PhoneSoft{
    @Override
    public void run() {
        System.out.println("手机游戏");
    }
}
/**
 * @author Shier
 * CreateTime 2023/5/17 17:16
 * 手机通讯录
 */
public class PhoneAddressList extends PhoneSoft {
    @Override
    public void run() {
        System.out.println("手机通讯录");
    }
}

手机品牌类:

/**
 * @author Shier
 * CreateTime 2023/5/17 17:17
 * 手机品牌
 */
public abstract class PhoneBrand {
    protected PhoneSoft phoneSoft;

    /**
     * 设置手机软件
     *
     * @param phoneSoft
     */
    public void setPhoneSoft(PhoneSoft phoneSoft) {
        this.phoneSoft = phoneSoft;
    }

    /**
     * 执行
     */
    public abstract void run();
}

手机品牌具体类M、N:

/**
 * @author Shier
 * CreateTime 2023/5/17 16:55
 * 手机品牌M
 */
public class PhoneBrandM extends PhoneBrand {
    @Override
    public void run() {
        System.out.print("品牌M");
        phoneSoft.run();
    }
}
/**
 * @author Shier
 * CreateTime 2023/5/17 16:55
 * 手机品牌N
 */
public class PhoneBrandN extends PhoneBrand {
    @Override
    public void run() {
        System.out.print("品牌N");
        phoneSoft.run();
    }
}

客户端代码:

/**
 * @author Shier
 * CreateTime 2023/5/17 16:59
 */
public class PhoneClient {
    public static void main(String[] args) {
        // 调用 M 品牌手机
        PhoneBrand brandM = new PhoneBrandM();
        // 调用通用的手机游戏
        brandM.setPhoneSoft(new PhoneGame());
        brandM.run();
        // 通讯录
        brandM.setPhoneSoft(new PhoneAddressList());
        brandM.run();

        // 调用N品牌手机
        PhoneBrand brandN = new PhoneBrandN();
        // 手机游戏
        brandN.setPhoneSoft(new PhoneGame());
        brandN.run();
        // 通讯录
        brandN.setPhoneSoft(new PhoneAddressList());
        brandN.run();
    }
}

输出结果:

相比之前不使用桥接模式和合成/聚合复用原则时,代码更加清晰了许多,而且如果需要新增一个功能时,比如手机音乐播放功能,那么只要增 加这个类就行了。不会影响其他任何类。类的个数增加也只是一个。

/**
 * @author Shier
 * CreateTime 2023/5/17 17:30
 * 手机播放音乐
 */
public class PhoneMusicPlay extends PhoneSoft {
    @Override
    public void run() {
        System.out.println("播放音乐");
    }
}

如果是要增加S品牌,只需要增加一个品牌子类就可以了。个数也是一个,不会影响其他类的改动。

/**
 * @author Shier
 * CreateTime 2023/5/17 17:31
 * 手机品牌S
 */
public class PhoneBrandS extends PhoneBrand {
    @Override
    public void run() {
        System.out.print("手机品牌S");
    }
}

这也符合了之前的开放-封闭原则。这样的设计显然不会修改原来的代码,而只是扩展类就行了。合成/聚合复用原则是优先使用对象的合成或聚合,而不是类继承。

3、桥接模式的总结

从上面的例子也看到了桥接模式的好处,但是也存在着一定的问题

优点:

  1. 解耦合:桥接模式将 抽象部分和实现部分解耦,使它们可以独立地变化和扩展。抽象部分和实现部分可以独立进行修改,而不会相互影响。
  2. 扩展性:桥接模式允许在抽象部分和实现部分中分别进行扩展。通过添加新的抽象部分或实现部分的子类,可以很容易地增加新的功能,而不需要修改现有的代码。
  3. 灵活性:桥接模式提供了一种灵活的设计方式,允许动态地切换和组合抽象部分和实现部分的实现。这使得系统更加灵活和可配置。

缺点:

  1. 增加复杂性:引入桥接模式会增加一些额外的类和接口,导致系统中的类数量增加,从而增加了代码的复杂性和理解成本。
  2. 增加开发成本:桥接模式需要对抽象部分和实现部分进行更加细致的设计和管理,这可能需要更多的开发时间和资源。

适用场景:

  1. 当一个系统需要在多个维度上进行扩展和变化时,可以使用桥接模式来解耦各个维度,使得抽象部分和实现部分可以独立地变化和扩展。
  2. 当一个抽象类有多个子类,而这些子类又有多个实现类时,可以使用桥接模式将抽象类与实现类解耦,使得抽象类和实现类可以独立地进行变化和扩展
  3. 当需要动态地切换抽象部分和实现部分的实现时,桥接模式提供了灵活性,允许在运行时动态地切换和组合不同的实现。

总而言之,桥接模式通过解耦抽象部分和实现部分,提供了灵活性、扩展性和可配置性。它适用于需要在多个维度上进行扩展和变化的系统,并且能够动态地切换和组合不同的实现。但要注意,桥接模式可能增加系统的复杂性和开发成本,需要进行细致的设计和管理。

现。这使得系统更加灵活和可配置。

缺点:

  1. 增加复杂性:引入桥接模式会增加一些额外的类和接口,导致系统中的类数量增加,从而增加了代码的复杂性和理解成本。
  2. 增加开发成本:桥接模式需要对抽象部分和实现部分进行更加细致的设计和管理,这可能需要更多的开发时间和资源。

适用场景:

  1. 当一个系统需要在多个维度上进行扩展和变化时,可以使用桥接模式来解耦各个维度,使得抽象部分和实现部分可以独立地变化和扩展。
  2. 当一个抽象类有多个子类,而这些子类又有多个实现类时,可以使用桥接模式将抽象类与实现类解耦,使得抽象类和实现类可以独立地进行变化和扩展
  3. 当需要动态地切换抽象部分和实现部分的实现时,桥接模式提供了灵活性,允许在运行时动态地切换和组合不同的实现。

总而言之,桥接模式通过解耦抽象部分和实现部分,提供了灵活性、扩展性和可配置性。它适用于需要在多个维度上进行扩展和变化的系统,并且能够动态地切换和组合不同的实现。但要注意,桥接模式可能增加系统的复杂性和开发成本,需要进行细致的设计和管理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/622791.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ssm+java+mysql在线捐赠系统

本系统实现一个在线捐赠系统,分为用户和管理员两种用户。具体功能描述如下: 后台管理员模块包括: 1. 系统用户管理:此功能为超级管理员所有,普通管理员没有此权限,实现超级管理员可以对普通管理员信息的…

如和使用matlab进行求导 ,入门级教程

文章目录 问题如图所示运行结果如图代码分析完整代码完结撒花 问题如图所示 运行结果如图 代码分析 % 定义样本数量 n 500;这行代码定义了一个变量 n,它代表样本数量。这个变量在后面的代码中会被用到。 % 将 s 和 z 取值范围分成子区间的个数 num_intervals 40…

MySQL数据库迁移到ORACLE(持续更新)

1. 使用Oracle SQL Developer 官方 SQL Developer 23.1下载 选择Windows 64-bit with JDK 11 included安装 2.下载后解压,选择exe执行启动,启动后见图 3. 创建连接 默认支持创建Oracle连接(见下图),第三方连接需导入…

企业微信自建应用 挂载网页步骤

打开企业微信网页端,并登录 企业微信 https://work.weixin.qq.com/wework_admin/frame#index 点击应用管理 再次点击 应用,划到自建版块,点击创建应用 依次添加应用信息 点击创建应用, 添加指定网页信息

【Android Studio】Flamingo版本 更新gradle插件(AGP) 7.+到8.+

步骤 build.gradle(module) android {namespace //adddefaultConfig {applicationId }}AndroidManifest.xml 取消package属性 <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas.android.com/apk/res/andr…

如何设置imagedraw.draw.text的字体大小

如何设置imagedraw.draw.text的字体大小 解决方法 虽然绘制框是draw.text() 但是这个函数没有提供修改的参数 解决方法 其实在字体中已经设置了大小了&#xff0c;他是按照图像调整的&#xff0c;我就直接修改了。 参考文章

QTableWidget自定义单元格

一 自定义QTableWidget 创建一个Widget项目&#xff0c;注释掉其中的ui->setupUi(this);使用自定义的布局。 #include "widget.h" #include "ui_widget.h" #include <QTableWidget> #include <QTableWidgetItem> #include <QLineEdit&…

Vue.js中的provide和inject方法是什么,有什么区别

Vue.js中的provide和inject方法 在Vue.js中&#xff0c;provide和inject是用于父组件向子组件传递数据的一种技术。通过使用provide和inject&#xff0c;我们可以在组件树中任意层次的组件之间进行数据的传递和共享&#xff0c;从而实现复杂的数据交互和状态管理的需求。本文将…

FANUC机器人MODBUS TCP通信配置方法(示教器实物演示)

FANUC机器人MODBUS TCP通信配置方法(示教器实物演示) 机器人一侧的配置: 如下图所示,示教器上找到设置—主机通讯, 如下图所示,选择第一项TCP/IP,点击详细进入配置界面, 如下图所示,设置机器人端口1#的IP地址为192.168.1.10,子网掩码:255.255.255.0 如下图所示…

【深入理解Linux内核锁】一、内核锁的由来

我的圈子&#xff1a; 高级工程师聚集地 我是董哥&#xff0c;高级嵌入式软件开发工程师&#xff0c;从事嵌入式Linux驱动开发和系统开发&#xff0c;曾就职于世界500强公司&#xff01; 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; …

Java Web——使用Filter实现用户登录

实验名称&#xff1a; 使用Filter实现用户登录 实验目的&#xff1a; &#xff08;1&#xff09;了解什么是Filter。 &#xff08;2&#xff09;熟悉Filter的拦截过程和接口中的方法。 &#xff08;3&#xff09;掌握第一个Filter程序的编写方法。 &#xff08;4&#xf…

华为荣获上海市技术发明一等奖!基于CANN的视频增强平台以AI技术修复历史视频

2023年5月26日&#xff0c;上海市科学技术奖励大会隆重召开&#xff0c;由上海交通大学牵头&#xff0c;中国科学院深圳先进技术研究院、咪咕视讯科技有限公司、华为技术有限公司、上海云视科技股份有限公司、上海人工智能创新中心、上海媒智科技有限公司、上海数字电视国家工程…

Oracle免费云设置Multi-factor Authentication

申请Oracle免费云账号的时候系统就强迫用户设置Multi-factor Authentication&#xff0c;我选择了OracleMobileAuthenticator作为2次认证工具。刚开始用还顺利&#xff0c;但用了一段时间后Oracle登录页面迟迟未向OracleMobileAuthenticator发送通知&#xff0c;要等非常久App才…

大模型 LLM 综述, A Survey of Large Language Models

大模型 LLM 综述, A Survey of Large Language Models 一、概述 一般认为NLP领域的大模型>10 Billion参数(也有人认为是6B、7B, 工业界用, 开始展现涌现能力); 经典大模型有GPT-3、BLOOM、Flan-T5、GPT-NeoX、OPT、GLM-130B、PaLM、LaMDA、LLaMA等; 大模型时间线, 图来自…

chatgpt赋能python:Python绘图教程:将画笔移动到绝对位置的方法

Python绘图教程&#xff1a;将画笔移动到绝对位置的方法 Python作为一门高级编程语言&#xff0c;设计初衷是让编程变得简单、易学、易用&#xff0c;且支持多种编程范式&#xff0c;其中产生了让人惊艳的绘图模块——Turtle&#xff08;海龟&#xff09;。 在这篇教程中&…

Mybatis-plus代码生成器

官网&#xff1a;MyBatis-Plus (baomidou.com) 顾名思义&#xff0c;就是它为你生成代码&#xff0c;这里可以为你生成Entity、Mapper、MapperXML、Service、controller等各个模块的代码&#xff0c;极大的提升开发效率 环境&#xff1a;mysql8.0.24、mybatis-plus3.5.3.1、spr…

<<Linux多线程服务端编程>>学习之栏1————线程安全的对象生命期管理

线程安全的对象生命期管理 此章节开头的前两句话&#xff0c;把我点醒&#xff0c;原来思考功力可以这么深厚&#xff01;如下&#xff1a; 第一句话&#xff1a; 编写线程安全的类不是难事&#xff0c; 用同步原语保护内部状态即可&#xff1b; 第二句话&#xff1a; 但是对…

【论文阅读】ControlNet

简介 目标&#xff1a;加入额外的条件&#xff08;例如边缘图像&#xff0c;深度图像&#xff09;控制生成的图像 现有挑战 特定领域上的数据较少&#xff0c;而预训练模型很大&#xff0c;很容易出现过拟合的情况。在资源有限的情况下&#xff0c;只能选择pretrain- finetun…

传统工业制造企业如何实现数字化转型?

传统工业制造企业如何实现数字化转型&#xff0c;以数字驱动、实现高价值管理&#xff1f; 传统企业实现数字化转型是一条很漫长但不得不走的道路&#xff0c;看到这个问题下有很多专业人士对传统企业如何做数字化转型都提出了专业的见解&#xff0c;所以这篇就以传统制造业为…

用ChatGPT来写高考作文,看看效果!

又是一年高考日&#xff0c;今天高考作文题目一出来&#xff0c;很多人第一时间就用AI进行写作&#xff0c;我这边也用gpt3和4分别生成了一篇文章&#xff0c;没有给他投喂范文&#xff0c;把要求和题目的prompt给它&#xff0c;让它分析和写作&#xff0c;来看看效果吧。 GPT4…