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

目录

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

概述

结构型设计模式

    结构型设计模式关注如何将类或对象按照某种方式组合成更大的结构,以解决系统结构和对象之间的耦合问题。其中,桥接模式(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语言中,可以使用接口和抽象类来实现桥接模式,更好地组织代码并解决系统结构和对象之间的耦合问题。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

希亦ACE和小吉内衣洗衣机选哪个?两款洗衣机对比

内衣洗衣机可以称得上是实现幸福的小家电,它不仅懒人的福音还是我们打工人的福音,在每天下班之后可以有时间休息了,洗完澡还有要手洗内衣裤,真的很痛苦,拥有了内衣洗衣机简直是一件非常幸福的事情,但现在市…

源码安装prometheus(普罗米修斯监控)

IP角色系统规格192.168.0.38Prometheus 服务端CentOS 74c8g192.168.0.25node_exporter 客户端CentOS 74c8g 普罗米修斯下载网址: Download | Prometheus 1.下载prometheus [rootprometheus opt]# wget https://github.com/prometheus/prometheus/releases/download/v2.47.2…

可以免费使用的设计素材网站分享

UI设计师最怕什么? 没有创意,没有灵感,没有思路! 在哪里可以得到idea?别担心,往下看! 你知道网络有多大,你想要什么吗?今天,我想和大家分享一些宝藏网页设…

榜首揭秘:热门产品原型设计工具TOP10

在现代产品制作和创新过程中,原型设计工具起着不可或缺的作用。这些工具为设计师、开发人员和团队提供了更深入的理解,以便核实和呈现他们的设计想法。根据项目的特性,可能需要使用各种类型的工具,因此,我们将在此篇文…

赛宁网安分靶场全力支持第三届“鹏城杯”攻防演练

为加速推进我国网络安全战略与数字化进程接轨,创新信息系统安全防护与网络安全技术研究模式,促进各行业网络安全建设的融合与协作,由鹏城实验室和中国网络空间安全人才教育论坛联合牵头举办的第三届“鹏城杯”联邦网络靶场协同攻防演练正式启…

MFA多因子认证

什么是多因子认证(MFA)?为什么需要MFA? 同义词 多因子认证或者多因素验证 [尤其是需要做等级保护测评的时候需要用到] 摘要 多因子认证MFA(Multi Factor Authentication)是一种安全认证过程,需…

使用宝塔面板安装mysql

1.第一步 在官网https://www.bt.cn/new/download.html下载页面直接在服务器控制面板复制这里的代码下载即可 2.第二步 下载好后按照服务器面版上有个公网地址,含有用户名和密码,保存好,然后通过公网地址打开一个网页,绑定自己注册…

恐怖地牢资产来袭!

我们将为您带来 VoxEdit 短片大赛的首场比赛!在 6 天内创建可用资产。 主题:设计与恐怖和地牢相关的资产。这些资产必须非常实用,不需要动画。(如果你愿意,你也可以制作动画)。 发挥你恐怖的一面,创造出适…

Hadoop学习总结(MapRdeuce的词频统计)

MapRdeuce编程示例——词频统计 一、MapRdeuce的词频统计的过程 二、编程过程 1、Mapper 组件 WordcountMapper.java package com.itcast.mrdemo;import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; …

傻瓜式提取网页数据的油猴脚本(JavaScript 代码)

目录 一、脚本适用范围 二、将脚本配置到浏览器 三、脚本用法 四、脚本原理 一、脚本适用范围 脚本适用范围:一次性提取任意网站的布局类似的数据,例如 淘宝的商品价格、微博的热搜标题、必应搜索的图片链接 脚本不适用范围:页面布局不相…

早安心语早读:愿我们都能活成自己喜欢的样子

1、时光,因爱而温润;岁月,因情而丰盈;愿我们都能活成自己喜欢的样子。 2、人有烦恼,就是因为记性太好,不该记的也抱着不放,背负太多,反而难以前进,追求,就会…

协同办公是什么?如何高效协同办公?读这篇就够了!

协同办公是指在工作中,团队成员通过共享信息、协作完成任务,实现高效的工作方式。它有助于促进团队成员之间的沟通、合作和协调,提升工作效率和质量。以下是一些实现高效协同办公的建议和好用的工具推荐。 首先,建立清晰的沟通渠…

mac中安装Homebrew

1、Homebrew是什么? 软件安装管理工具 2、先检查电脑中是否已经安装了Homebrew 打开终端输入:brew 提示命令没有找到,说明电脑没有安装Homebrew 如果提示上述图片说明Homebrew已经安装成功 3、安装Homebrew 进入https://brew.sh/ 复制的命…

企业云盘:企业文件数据存储与共享的全面解决方案

企业云盘为企业的文件数据的存储与管理提供了一种安全、高效又便捷的解决方案。在企业文件数据存储管理上,企业云盘有什么优势吗?本文将重点分析企业云盘的优点和好处! 一、安全性 对于企业文件数据管理工具,安全是首位。与个人…

C语言链表

1.链表 什么是链表 链表是一种数据结构&#xff0c;是一种数据存放的思想 数组特点&#xff1a;元素地址连续 数组的缺点:增加、删除、改、查比较困难&#xff0c;特别是增加的时候&#xff0c;不够灵活 链表的每一项都是一个结构体 #include<stdio.h> struct Test…

智慧工地解决方案,实现安全预警、机械智能监控、作业指导、绿色施工、劳务管理、工程进度监控、施工质量检查

智慧工地云平台全套源码 智慧工地平台采用先进的云计算、物联网和大数据技术&#xff0c;可以实现智慧工地方案的落地。能够实现实时掌控工地活动及各项进度&#xff0c;有效预防违章施工。能够为工地提供多项服务&#xff0c;如安全预警、机械智能监控、作业指导、绿色施工、劳…

Java项目实战《苍穹外卖》 一、项目概述

人道洛阳花似锦&#xff0c;偏我来时不逢春。 系列文章目录 苍穹外卖是黑马程序员2023年的Java实战项目&#xff0c;作为业余练手用&#xff0c;需要源码或者课程的可以找我&#xff0c;无偿分享 Java项目实战《苍穹外卖》 一、项目概述Java项目实战《苍穹外卖》 二、项目搭建 …

NewStarCTF2023 Reverse Week3---Let‘s Go Wp

分析 程序打开后结合题目可以发现是 GO语言。 在GO语言中&#xff0c;main_init 要先于 main 函数运行。 在这里对一个iv做了处理。 用插件Signsrch发现AES加密 知道是AES后&#xff0c;就需要找密文&#xff0c;key和iv了。 密文应该就是前面的十六进制字符串。 key和i…

springboot使用MongoTemplate根据正则表达式查询日期数据

一、日期正则表达式测试 匹配HH:mm:ss正则表达式写法有很多列举两个 .(点)代表任意匹配 ^必须以xxx开头, 如^[a-z],必须以a-z的字母开头 : 精确匹配,必须是: ([0-1]?[0-9]|2[0-3]).([0-5][0-9]).([0-5][0-9]) ^([0-1]?[0-9]|2[0-3]).([0-5][0-9]).([0-5][0-9])$ ([0-1]?…

UniPro提高集成能力 让客户专注于交付价值

一千个哈姆莱特就有一千个读者&#xff0c;一千个开发团队&#xff0c;也会有各不相同的软件工具和工作流程。工具与工具之间&#xff0c;功能上的割裂亦或重叠&#xff0c;都会给企业和团队的协作带来阻塞&#xff0c;结果就会导致团队之间各自为战、信息孤岛的形成以及资源的…