设计模式之桥接模式--连接抽象与实现(你想知道的问题都有)

news2024/12/28 3:42:05

目录

  • 概述
    • 结构型设计模式
    • 桥接模式的定义
    • 桥接模式的角色和关系
  • 版本迭代
    • 紧耦合版
    • 增加品牌
    • 两个品牌两款软件
    • 松耦合的设计
    • 版本迭代业务分析总结
  • 问题升华
    • 抽象与实现
    • 抽象包含的一些方法或属性依赖于实现部分的接口
    • 关联关系与桥接模式
    • 桥接模式适合情况
    • 谁是实现,谁是抽象
    • 组合聚合的好处
  • 总结

概述

结构型设计模式

    结构型设计模式关注如何将类或对象按照某种方式组合成更大的结构,以解决系统结构和对象之间的耦合问题。其中,桥接模式(Bridge Pattern)是一种重要的结构型设计模式,它通过将抽象部分与实现部分分离,以便二者可以独立地变化。

桥接模式的定义

    桥接模式是一种用于将抽象部分与实现部分分离的设计模式。它的主要目的是通过将抽象和实现独立地变化,以便二者可以独立地扩展。桥接模式通过将抽象部分和实现部分解耦,使它们可以独立地变化,从而提高代码的灵活性和可扩展性。

桥接模式的角色和关系

  • Abstraction(抽象部分):定义了高层逻辑,维护一个指向Implementor对象的引用。
  • RefinedAbstraction(被提炼的抽象):对Abstraction进行扩展,可以增加额外的操作或功能。
  • Implementor(实现部分):定义了具体实现的接口,提供了基本操作的方法。
  • ConcreteImplementor(具体实现部分):实现Implementor接口,提供具体实现。

在这里插入图片描述

版本迭代

业务需求
某品牌手机想安装一款游戏
在这里插入图片描述

紧耦合版

public class HandsetBrandNGame {
    public void run(){
        System.out.println("运行N品牌手机游戏 ");
    }
}

//客户端
public class Client {
    public static void main(String[] args) {
        HandsetBrandNGame game=new HandsetBrandNGame();
        game.run();
    }
}

增加品牌

新的需求是两个品牌的手机都要安装游戏
要增加父类了
在这里插入图片描述

//父类
public class HandsetGame {
    public void run(){}
}

//N品牌手机
public class HandsetBrandNGame extends HandsetGame{
    public void run(){
        System.out.println("运行N品牌手机游戏");

    }
}

//m品牌手机
public class HandsetBrandMGame extends HandsetGame{
    public void run(){
        System.out.println("运行M品牌手机游戏");

    }
}

//客户端
public class Client {
    public static void main(String[] args) {
        HandsetGame M=new HandsetBrandMGame();
        M.run();

        HandsetGame N=new HandsetBrandNGame();
        N.run();
    }
}

当你发现代码写起来太简单的时候,那么问题就来喽

两个品牌两款软件

    新的需求是两个品牌的手机都安装游戏和通讯录
在这里插入图片描述

//手机父类
public class HandsetBrand {
    public void run(){
    }
}

//M品牌手机
public class HandsetBrandM extends HandsetBrand {
}

//N品牌手机
public class HandsetBrandN extends HandsetBrand {
}

    写到这有没有发现两个子类完全继承父类方法,没有扩充也没有重写,这就很有问题

//游戏
public class HandsetBrandMGame extends HandsetBrandM {
    public void run(){
        System.out.println("运行M品牌手机游戏");
    }
}

public class HandsetBrandNGame extends HandsetBrandM {
    public void run(){
        System.out.println("运行N品牌手机游戏");
    }
}

//通讯录
public class HandsetBrandMAddressList extends HandsetBrandM {
    public void run(){
        System.out.println("运行M品牌手机通讯录");
    }
}

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

//客户端
public class Client {
    public static void main(String[] args) {
        HandsetBrand ab;
        ab=new HandsetBrandMAddressList();
        ab.run();

        ab=new HandsetBrandMGame();
        ab.run();

        ab=new HandsetBrandNAddressList();
        ab.run();

        ab=new HandsetBrandNGame();
        ab.run();
    }
}

    这是从手机品牌的维度去设计,如果从软件的维度去设计,类图如下
在这里插入图片描述
    可以看到和上面的类图一样,只是父类变成了软件,子类变成了游戏和通讯录

    换个中文的图可能理解更清晰
在这里插入图片描述
在这里插入图片描述

    这两种分类只是从两个角度,但是对于增加新的品牌或者增加新的软件来说都需要增加一坨(这个词有没有画面,哈哈),对于软件设计来说都不是好的方向。

    两个毫不相干的功能想掺和到一起,通常可以考虑用继承去实现,继承作用的一个角度就是增加新的功能,但是对象的继承关系是在编译时就定义好了,无法在运行时改变从父类继承的实现。子类的实现与它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。当需要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。

    那有其他办法可以解决这种通过继承而增加新的功能的方法吗,可以考虑组合聚合关系。

    组合聚合都是关联的特殊种类。聚合表示一种弱的‘拥有’关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分:组合则是一种强的‘拥有’关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。那么接下来一版就通过聚合关系来解决扩充复用和减弱耦合的问题.

松耦合的设计

在这里插入图片描述

手机软件

//手机软件父类
public abstract class HandsetSoft {
    public abstract void run();
}

//具体软件子类-通讯录
public class HandsetAddressList extends HandsetSoft{
    @Override
    public void run() {
        System.out.println("通讯录");
    }
}

//游戏
public class HandsetGame extends HandsetSoft{
    @Override
    public void run() {
        System.out.println("手机游戏");
    }
}

手机品牌

//手机品牌父类
public abstract class HandsetBrand {
    protected HandsetSoft soft;
    //设置手机软件
    public void setHandsetSoft(HandsetSoft soft){
        this.soft=soft;
    }
    //运行
    public abstract void run();
}

//具体品牌子类
public class HandsetBrandM extends HandsetBrand{
    @Override
    public void run() {
        System.out.println("品牌M");
        soft.run();
    }
}

public class HandsetBrandN extends HandsetBrand{
    @Override
    public void run() {
        System.out.println("品牌N");
        soft.run();
    }
}

客户端


public class Client {
    public static void main(String[] args) {
        HandsetBrand ab;
        ab=new HandsetBrandM();//M品牌

        ab.setHandsetSoft(new HandsetGame());
        ab.run();

        ab.setHandsetSoft(new HandsetAddressList());
        ab.run();


        HandsetBrand ab2;
        ab2=new HandsetBrandN();//N品牌

        ab2.setHandsetSoft(new HandsetGame());
        ab2.run();

        ab2.setHandsetSoft(new HandsetAddressList());
        ab2.run();

    }
}

版本迭代业务分析总结

    从紧耦合到使用桥接模式,是从有限到无限思维的变化,最后再来看一下手机和软件这个业务,使用桥接模式的业务需求主要是解决手机软件和手机品牌之间的耦合关系,以实现它们可以独立地变化和扩展。具体来说,手机软件和手机品牌使用桥接模式的业务需求包括以下几个方面:

  • 独立变化:手机软件和手机品牌属于两个不同的维度,它们可能会因为不同的需求而需要独立变化。比如,手机软件可能需要在不同的手机品牌上运行,而手机品牌可能需要支持不同的手机软件。使用桥接模式可以让它们之间的变化相互独立,不会相互影响。

  • 灵活组合:用户希望能够根据个人喜好和需求自由地组合手机软件和手机品牌,而不受限于固定的组合方式。使用桥接模式可以让用户根据需要灵活地组合不同的手机软件和手机品牌,而不需要为每一种组合编写大量的代码。

  • 代码复用:桥接模式能够提高代码的复用性,避免在不同的手机软件和手机品牌组合下重复编写相似的代码。通过将抽象部分与实现部分分离,可以更好地复用已有的代码,减少开发成本和维护成本。

  • 扩展性:随着新的手机软件和手机品牌不断涌现,系统需要具有良好的扩展性,能够方便地加入新的手机软件和手机品牌。使用桥接模式可以使系统具有较高的扩展性,能够快速、灵活地适应新的需求。

    总的来说,手机软件和手机品牌使用桥接模式的业务需求是为了实现它们之间的独立变化、灵活组合、代码复用和良好的扩展性,从而提高系统的灵活性、可维护性和可扩展性。

问题升华

抽象与实现

    桥接概念中的“通过将抽象部分和实现部分解耦,使它们可以独立地变化”什么意思呢,抽象与它的实现分离,这并不是说,让抽象类与其派生类分离,因为这没有任何意义。实现指的是抽象类和它的派生类用来实现自己的对象。就刚才的例子而言,就是让‘手机’既可以按照品牌来分类,也可以按照功能来分类。

    再专业一点解释"抽象" 和 “实现” 这两个术语是:

  • 抽象(Abstraction):代表系统中的高层结构,它提供了一组抽象接口,并维护对实现部分的引用。抽象可以包含一些方法或属性,这些方法或属性依赖于实现部分的接口。

  • 实现(Implementation):代表抽象接口的实现部分,它定义了实现部分的接口。实现部分通常提供了一组具体的操作,这些操作被抽象部分所引用和使用。

    举例来说,假设要设计一个绘制图形的系统,其中抽象部分可以是各种形状(如圆形、矩形等),而实现部分可以是各种颜色(如红色、蓝色等)。这样,就可以将抽象的形状和实现的颜色进行组合,从而实现不同形状和不同颜色的组合绘制。

    除了上面提到的手机的例子,生活中还有许多常见的例子:

    比如桥接模式可以应用于多媒体播放器,其中抽象部分可以是各种播放器类型(如音频播放器、视频播放器),而实现部分可以是各种操作系统平台(如Windows、MacOS、Linux)。
也可以应用于汽车的制造,其中抽象部分可以是汽车的类型(如轿车、卡车),而实现部分可以是发动机类型(如汽油发动机、电动发动机)。

抽象包含的一些方法或属性依赖于实现部分的接口

    举个例子,假设有一个形状的抽象类 Shape,它包含一个绘制的抽象方法:

public abstract class Shape {
    protected DrawAPI drawAPI;

    protected Shape(DrawAPI drawAPI) {
        this.drawAPI = drawAPI;
    }

    public abstract void draw();
}

    在这里,Shape 类中的 draw 方法是一个抽象方法,它依赖于 DrawAPI 接口,但并没有具体的实现。DrawAPI 接口代表了实现部分的接口,它定义了绘制的方法:

public interface DrawAPI {
    void draw(int x, int y);
}

    具体的形状类(如 Circle、Square)会继承 Shape 类并实现 draw 方法,而在实现 draw 方法时,会调用 DrawAPI 接口的方法来完成具体的绘制操作。

    通过这种方式,抽象部分中的方法依赖于实现部分的接口,具体的实现则由实现部分来完成,从而实现了抽象部分与实现部分的解耦。这样的设计使得我们可以根据需要灵活地变更和扩展具体的实现部分,而不会影响到抽象部分的设计和使用。

关联关系与桥接模式

    抽象部分中的方法依赖于实现部分的接口,可以通过关联关系来实现,但它也是桥接模式的一个特点。

    在桥接模式中,抽象部分和实现部分之间存在关联关系,但它们又可以独立地变化和扩展。具体来说,抽象部分提供了一组抽象接口,而实现部分定义了具体的实现。抽象部分通过引用实现部分的接口来使用具体的实现。

    关联关系是桥接模式的一种实现方式,通过关联关系,抽象部分和实现部分之间建立了联系。但桥接模式并不仅限于关联关系,它更重要的是将抽象部分和实现部分解耦,使它们可以独立地变化

    在桥接模式中,=可以根据需要灵活地变更和扩展抽象部分和实现部分,而不会相互影响,因此,当抽象部分中的方法依赖于实现部分的接口,并且能够实现抽象部分和实现部分的解耦时,可以说符合桥接模式的设计原则。关联关系是一种常见的实现方式,但并不是唯一的方式。

桥接模式适合情况

    一个产品只要有不同的分类方式,就可以考虑用桥接。

    当一个类存在两个独立变化的维度时,可以使用桥接模式来将这两个维度分离,使它们可以独立地变化。例如,一个产品可以按照品牌和功能进行分类,这样就可以使用桥接模式将品牌和功能这两个维度进行分离,使它们可以独立地变化。

    当一个类需要在多个维度上进行扩展,且不希望使用多层继承结构时,可以考虑使用桥接模式。桥接模式可以避免多层继承结构带来的复杂性和耦合度,使得系统更加灵活和可扩展。

谁是实现,谁是抽象

    上面提到多个维度扩展,那如何定义谁是抽象谁是实现呢,就好比上面的手机品牌和手机软件

    确定抽象和实现的关系取决于我们希望如何组织和管理不同维度的变化。一般来说,可以根据以下几点来确定哪种是抽象,哪种是实现:

稳定性和变化性:如果一个维度的变化相对稳定且不太频繁,而另一个维度的变化比较频繁,通常会将稳定的部分作为抽象,而变化的部分作为实现。

扩展性:如果希望在某个维度上进行扩展,而在另一个维度上保持相对稳定,通常会将需要扩展的部分作为抽象,而相对稳定的部分作为实现。

复用性:如果希望将某个维度的实现应用到多个抽象中,通常会将这个维度的实现作为独立的实现部分。

解耦合:如果希望将不同维度之间进行解耦,使它们可以独立变化,通常会将不同的维度分别作为抽象和实现。

    还是上面的例子,可以按照品牌和功能两个维度进行分类。如果认为品牌相对稳定而功能比较容易变化,可以将品牌作为抽象部分,功能作为实现部分;反之,如果功能相对稳定而品牌经常变化,可以将功能作为抽象部分,品牌作为实现部分。

组合聚合的好处

    组合聚合原则的好处是,优先使用对象的合成/聚合将有助于保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。

总结一下:
灵活性:通过桥接模式可以让抽象部分和实现部分独立变化,而组合和聚合也能够让对象之间的关系更加灵活。

可维护性:松耦合的设计使得系统更容易维护和扩展。

复用性:桥接模式和组合、聚合都有利于提高代码的复用性,使得系统更易于重用。

相对而言,继承可能会导致类之间的强耦合,而且继承层次的过深会增加系统的复杂性,降低系统的灵活性和可维护性。因此,在很多情况下,桥接模式、组合和聚合往往比继承更具优势。

总结

    桥接模式是一种非常有用的设计模式,它可以将抽象和实现独立地变化,从而提高代码的灵活性和可扩展性。在Java语言中,可以使用接口和抽象类来实现桥接模式,更好地组织代码并解决系统结构和对象之间的耦合问题。

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

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

相关文章

vscode设置latex

vscode配置latex 1.安装vscode,并添加环境变量路径 2.安装latex,bin文件夹添加到环境变量路径 3.vscode安装插件 4.vscode->文件->首选项->显示配置内容->setting.json文件,查看其位置目录,通过我的电脑找到此文件(不要使用v…

torch - 张量Tensor常见的形式

1.Scalar 通常就是一个数值 x tensor(42.) 输出x: 2.Vector 特征向量 例如:[-5., 2., 0.]在深度学习中通常表示特征,如词向量特征,某一维度特征等 3.Matrix 一般计算的都是矩阵,通常都是多维的。 可以做矩阵的…

C# +.Net检验科信息管理系统源码 LIS系统源码

检验科信息管理系统(LIS) LIS系统集申请、采样、核收、计费、检验、审核、发布、质控、查询、耗材控制等检验科工作为一体的网络管理系统。它的开发和应用将加快检验科管理的统一化、网络化、标准化的进程。 主要包括以下功能: 1、数据采集…

【Python】集合与字典

按照输入顺序输出 将输入的名字去重,同时按照输入顺序输出 sinput().split(,) blist(set(s)) bsorted(b,keys.index) print(b) 删除集合元素、更新集合 根据操作删除更新集合 update括号里可以是一个集合,add只能是一个元素 discard用于删除元素&#x…

Fedora 项目近日发布了 Fedora Linux 39

导读几经推迟之后,Fedora 项目近日发布了 Fedora Linux 39,这是红帽公司赞助的面向大众的 GNU/Linux 发行版的最新稳定版本,采用了最新的技术和开源应用程序。 Fedora Linux 39 由 Linux 内核 6.5 支持,并提供了一些最新的桌面环境…

APP安全加固怎么做?加固技术、加固方法、加固方案

前面的文章中我们为大家介绍了移动应用安全检测的测试依据、测试方法、和测试内容,本文我们着重分享App安全加固的相关内容。 (安全检测内容) 通过前面的文章我们知道了app安全检测要去检测哪些内容,发现问题后我们如何去修复&am…

mfc140u.dll丢失的解决方法,以及针对每个解决mfc140u.dll丢失办法的优缺点

在使用电脑的过程中,有时会遇到一些与动态链接库文件(DLL)相关的错误。其中,mfc140u.dll丢失是一种常见的问题,它可能导致应用程序无法正常运行。在本文中,我们将探讨关于mfc140u.dll丢失的解决办法&#x…

1.数字化转型概述

中台数字化转型的问题 在中台数字化转型过程中,如何进行业务领域边界划分,如何完成中台领域建模实现能力复用,如何完成单体应用拆分利微服务设计,如何实现前中后台的协同设计; DDD解决的问题 DDD首先从业务领域人手&#xff0c…

linux网络编程之TCP协议编程

Linux网络编程之TCP协议编程 tcp协议编程模型socket函数sockaddr_inbindlistenconnect 应用服务端代码客服端代码 TCP协议编程) tcp协议编程模型 Server 1.创建socket (socket函数) 2.确定服务器协议地址簇 (struct sockaddr) 3.绑定 (bind) 4.监听 ( listen) 5.接受客户端连接…

人脸识别4G执法记录仪、一体化智能AI布控球在智慧社区、智能网格中的应用

智慧社区守护者:人脸识别与智能监控技术的融合创新 随着城市的飞速发展和科技的不断进步,智慧社区和智能网格的概念已经成为现代城市管理的一个重要趋势。在这一过程中,人脸识别技术、4G执法记录仪以及一体化智能AI布控球等智能监控设备&…

字节跳动小程序开发:探索创新的数字化世界

在数字化时代,字节跳动小程序开发成为企业数字化转型的关键一环。通过这一平台,企业能够借助先进的技术和丰富的功能,实现创新、引领市场潮流。本文将通过一些简单的技术代码示例,带你深入了解字节跳动小程序开发的魅力。 1. 小…

无需公网IP,使用MCSM面板一键搭建我的世界Minecraft服务器联机游戏

文章目录 前言1.Mcsmanager安装2.创建Minecraft服务器3.本地测试联机4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射内网端口 5.远程联机测试6. 配置固定远程联机端口地址6.1 保留一个固定TCP地址6.2 配置固定TCP地址 7. 使用固定公网地址远程联机 前言 MCSManager是一个…

初识Scrapy:Python中的网页抓取神器

Scrapy是一个基于Python的快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。它广泛应用于数据挖掘、监测和自动化测试等领域。Scrapy的强大之处在于它是一个框架,可以根据实际需求进行修改和扩展。 Scrapy的主要特点 …

智慧物流追踪:打造未来的物流网络

随着互联网和物流行业的深度融合,智慧物流已成为现代物流发展的新趋势。通过开发一款智能化的物流追踪app小程序,我们不仅可以提高物流效率,还可以为客户提供更加便捷的服务。本文将从市场需求、技术应用、竞争优势、行业前景等方面对智慧物流…

11月24日 AI+软件研发数字峰会(AiDD)即将启航!

▼ 伴随着人工智能(AI,特别是大语言模型)在众多行业领域的广泛应用及其带来的颠覆性变革,软件的开发模式、方式和实践都可能会发生巨大的变化。为助力更多企业在人工智能的浪潮中乘风破浪,“AI软件研发数字峰会&#x…

如何快速下载微信视频号的视频?简单几步轻松搞定

在当今社交媒体充满活力的时代,微信视频号已经成为许多用户分享生活、创意和知识的新舞台。然而,随着互联网信息爆炸式增长,有时我们可能会错过一些精彩的视频内容。这时,如何快速下载微信视频号的视频成为了许多用户关心的话题。…

企业数字化过程中数据仓库与商业智能的目标

当前环境下,各领域企业通过数字化相关的一切技术,以数据为基础、以用户为核心,创建一种新的,或对现有商业模式进行重塑就是数字化转型。这种数字化转型给企业带来的效果就像是一次重构,会对企业的业务流程、思维文化、…

TCP-Modbus通信过程解析与实例演示

大家好!今天我将简要介绍一下如何使用TCP-Modbus方式与信捷PLC(XD5E型号)进行通讯,并演示整个过程。 TCP-Modbus通信过程解析与实例演示 首先,我们需要在PLC软件中设置相应的IP地址。PLC作为服务器,我们将其…

6.2 List和Set接口

1. List接口 List接口继承自Collection接口,List接口实例中允许存储重复的元素,所有的元素以线性方式进行存储。在程序中可以通过索引访问List接口实例中存储的元素。另外,List接口实例中存储的元素是有序的,即元素的存入顺序和取…

VS2017 IDE 编译时的 X86、x64位 是干什么的

指定编译出的程序是x86架构下的32位程序还是64位程序 VS2017项目配置X86改配置x64位_winform:把项目由x86改为x64-CSDN博客 vs平台选项:Any CPU,x86,x64_vs anycpu-CSDN博客