【设计模式——学习笔记】23种设计模式——桥接模式Bridge(原理讲解+应用场景介绍+案例介绍+Java代码实现)

news2024/10/2 14:30:11

问题引入

现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网,打电话等),如图

在这里插入图片描述

【对应类图】

在这里插入图片描述

【分析】

  • 扩展性问题(类爆炸),如果我们再增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。
  • 违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这样增加了代码维护成本
  • 解决方案:使用桥接模式

介绍

基础介绍

  • 桥接模式(Bridge模式)是指: 将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。
  • Bridge 的意思是“桥梁”。就像在现实世界中,桥梁的功能是将河流的两侧连接起来一样Bridge 模式的作用也是将两样东西连接起来,它们分别是类的功能层次结构和类的实现层次结构。
  • 是一种结构型设计模式
  • Bridge模式基于类的最小设计原则(实现功能的同时,让类尽可能少),通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(lmplementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展

类的功能层次结构

假设现在有一个类Something。当我们想在Something中增加新功能时(想增加一个具体方法时),会编写一个Something类的子类(派生类),如SomethingGood类。这样就构成了一个小小的类层次结构。

在这里插入图片描述

父类本身具备一些基本功能,子类在继承父类的功能之外,还可以添加新的功能。这种层次结构被称为“类的功能层次结构”。如果需要在SomethingGood的基础上继续增加新的功能,再写SomethingGood的子类即可。

在这里插入图片描述

注意:类的层次结构关系不应当过深

类的实现层次结构

抽象类声明了一些抽象方法,定义了接口(API),然后子类负责去实现这些抽象方法。父类的任务是通过声明抽象方法的方式定义接口(API),而子类的任务是实现抽象方法。正是由于父类和子类的这种任务分担,我们才可以编写出具有高可替换性的类。

当子类Concreteclass实现了父类Abstractclass类的抽象方法时,它们之间就构成了一个小小的层次结构。

在这里插入图片描述

这种类的层次结构(类的实现层次结构)并非用于增加功能,并不方便我们增加新的方法。它的真正作用是帮助我们实现下面这样的任务分担

  • 类通过声明抽象方法来定义接口(API)
  • 子类通过实现具体方法来实现接口(API)

一个抽象类可以有多种子实现类

在这里插入图片描述

层次结构分离

当类的层次结构只有一层时,功能层次结构与实现层次结构是混杂在一个层次结构中的。这样很容易使类的层次结构变得复杂,也难以透彻地理解类的层次结构。因为自己难以确定究竟应该在类的哪一个层次结构中去增加子类。

因此,我们需要将“类的功能层次结构”与“类的实现层次结构”分离为两个独立的类层次结构。当然,如果只是简单地将它们分开,两者之间必然会缺少联系。所以我们还需要在它们之间搭建一座桥梁,这就是桥接模式。

类图

在这里插入图片描述

抽象类和接口是聚合关系,也是调用和被调用关系

应用场景

对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用,如以下场景:

  • JDBC驱动程序
  • 银行转账系统
    • 转账分类: 网上转账,柜台转账,AMT转账
    • 转账用户类型: 普通用户,银卡用户,金卡用户
  • 消息管理
    • 消息类型: 即时消息,延时消息
    • 消息分类: 手机短信,邮件消息,QQ消息…

案例

案例一

类图

在这里插入图片描述

代码实现

【接口】

package com.atguigu.bridge;

/**
 * 接口
 */
public interface Brand {
   void open();
   void close();
   void call();
}

【接口实现类:小米手机】

package com.atguigu.bridge;

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(" 小米手机打电话 ");
   }

}

【接口实现类:Vivo手机】

package com.atguigu.bridge;

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.atguigu.bridge;

public abstract class Phone {

   /**
    * 聚合品牌
    */
   private Brand brand;

   /**
    * 构造器
    * @param brand
    */
   public Phone(Brand brand) {
      super();
      this.brand = brand;
   }

   protected void open() {
      this.brand.open();
   }
   protected void close() {
      this.brand.close();
   }
   protected void call() {
      this.brand.call();
   }

}

【抽象类子类:折叠手机】

package com.atguigu.bridge;


/**
 * 折叠式手机类,继承 抽象类 Phone
 */
public class FoldPhone extends Phone {

   //构造器
   public FoldPhone(Brand brand) {
      super(brand);
   }

   public void open() {
      System.out.println(" 折叠样式手机 ");
      // 实际上调用的是具体品牌(如Xiaomi)的开机方法,抽象类Phone充当桥接作用
      super.open();
   }

   public void close() {
      System.out.println(" 折叠样式手机 ");
      super.close();
   }

   public void call() {
      System.out.println(" 折叠样式手机 ");
      super.call();
   }
}

【抽象类子类:直立手机】

package com.atguigu.bridge;

public class UpRightPhone extends Phone {

   //构造器
   public UpRightPhone(Brand brand) {
      super(brand);
   }

   public void open() {
      System.out.println(" 直立样式手机 ");
      super.open();
   }

   public void close() {
      System.out.println(" 直立样式手机 ");
      super.close();
   }

   public void call() {
      System.out.println(" 直立样式手机 ");
      super.call();
   }
}

【客户端】

package com.atguigu.bridge;

public class Client {

   public static void main(String[] args) {

      //获取折叠式手机 (样式 + 品牌 才是具体的手机)
      Phone phone1 = new FoldPhone(new XiaoMi());
      phone1.open();
      phone1.call();
      phone1.close();

      System.out.println("==============");

      Phone phone2 = new FoldPhone(new Vivo());
      phone2.open();
      phone2.call();
      phone2.close();

      System.out.println("==============");

      UpRightPhone phone3 = new UpRightPhone(new XiaoMi());
      phone3.open();
      phone3.call();
      phone3.close();

      System.out.println("==============");

      UpRightPhone phone4 = new UpRightPhone(new Vivo());
      phone4.open();
      phone4.call();
      phone4.close();
   }

}

【运行】

折叠样式手机 
 小米手机开机 
 折叠样式手机 
 小米手机打电话 
 折叠样式手机 
 小米手机关机 
==============
 折叠样式手机 
 Vivo手机开机 
 折叠样式手机 
 Vivo手机打电话 
 折叠样式手机 
 Vivo手机关机 
==============
 直立样式手机 
 小米手机开机 
 直立样式手机 
 小米手机打电话 
 直立样式手机 
 小米手机关机 
==============
 直立样式手机 
 Vivo手机开机 
 直立样式手机 
 Vivo手机打电话 
 直立样式手机 
 Vivo手机关机 

Process finished with exit code 0

分析

  • 无论增加一个样式或者增加一个新的手机品牌,都只需要增加一个类

案例二

类图

在这里插入图片描述

Display使用DispalyImpl的方法来完成功能,StringDisplayImpl负责DispalyImpl方法的具体实现,CountDisplay用来完成更多的功能

代码实现

【类的功能层次结构:Display】

package com.atguigu.bridge.Sample;

/**
 * 类的功能层次最上层
 */
public class Display {
    /**
     * 实现了Display类的具体功能的实例 (桥梁)
     */
    private DisplayImpl impl;
    public Display(DisplayImpl impl) {
        this.impl = impl;
    }

    /**
     * 显示前的处理
     */
    public void open() {
        impl.rawOpen();
    }

    /**
     * 显示处理
     */
    public void print() {
        impl.rawPrint();
    }

    /**
     * 显示后的处理
     */
    public void close() {
        impl.rawClose();
    }

    /**
     * 调用上面的三个方法来进行显示
     */
    public final void display() {
        open();
        print();                    
        close();
    }
}

private DisplayImpl impl;使用了“委托”关系,而不是使用“继承”关系。继承是强关联关系,委托是弱关联关系。使用委托更方便扩展。

【类的功能层次结构:CountDisplay】

package com.atguigu.bridge.Sample;

/**
 * 在Display类的基础上增加新功能
 */
public class CountDisplay extends Display {
    public CountDisplay(DisplayImpl impl) {
        super(impl);
    }

    /**
     * 循环显示times次
     *
     * @param times
     */
    public void multiDisplay(int times) {
        open();
        for (int i = 0; i < times; i++) {
            print();
        }
        close();
    }
}

【类的实现层次结构:上层】

package com.atguigu.bridge.Sample;

/**
 * 类的实现层次最上层
 */
public abstract class DisplayImpl {
    public abstract void rawOpen();
    public abstract void rawPrint();
    public abstract void rawClose();
}

【类的实现层次结构:下层】

package com.atguigu.bridge.Sample;

/**
 * 类的实现层次结构,具体实现类
 */
public class StringDisplayImpl extends DisplayImpl {
    /**
     * 要显示的字符串
     */
    private String string;
    /**
     * 以字节单位计算出的字符串的宽度
     */
    private int width;

    /**
     * 构造方法
     *
     * @param string 构造函数接收要显示的字符串string
     */
    public StringDisplayImpl(String string) {
        // 将它保存在字段中
        this.string = string;
        // 把字符串的宽度也保存在字段中,以供使用
        this.width = string.getBytes().length;
    }

    public void rawOpen() {
        printLine();
    }

    public void rawPrint() {
        // 前后加上"|"并显示
        System.out.println("|" + string + "|");
    }

    public void rawClose() {
        printLine();
    }

    private void printLine() {
        // 显示用来表示方框的角的"+"
        System.out.print("+");
        // 显示width个"-"
        for (int i = 0; i < width; i++) {
            // 将其用作方框的边框
            System.out.print("-");                    
        }
        // 显示用来表示方框的角的"+"
        System.out.println("+");
    }
}

【主类】

package com.atguigu.bridge.Sample;

public class Main {
    public static void main(String[] args) {
        Display d1 = new Display(new StringDisplayImpl("Hello, China."));
        Display d2 = new CountDisplay(new StringDisplayImpl("Hello, World."));
        CountDisplay d3 = new CountDisplay(new StringDisplayImpl("Hello, Universe."));
        d1.display();
        System.out.println();
        d2.display();
        System.out.println();
        d3.display();
        d3.multiDisplay(5);
    }
}

【运行】

+-------------+
|Hello, China.|
+-------------+

+-------------+
|Hello, World.|
+-------------+

+----------------+
|Hello, Universe.|
+----------------+
+----------------+
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
+----------------+

Process finished with exit code 0

拓展一

在上述示例程序中增加一个类,实现“显示字符串若干(随机)次”的功能。

【新增类】

package com.atguigu.bridge.A1;

import java.util.Random;

public class RandomCountDisplay extends CountDisplay {
    private Random random = new Random();

    public RandomCountDisplay(DisplayImpl impl) {
        super(impl);
    }

    public void randomDisplay(int times) {
        multiDisplay(random.nextInt(times));
    }
}

【主类】

package com.atguigu.bridge.A1;

public class Main {
    public static void main(String[] args) {
        RandomCountDisplay d = new RandomCountDisplay(new StringDisplayImpl("Hello, China."));
        d.randomDisplay(10);
    }
}

扩展二

在上述示例程序中增加一个类,实现“显示文本文件的内容”的功能。

【新增类】

package com.atguigu.bridge.A2;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class FileDisplayImpl extends DisplayImpl {
    private String filename;
    private BufferedReader reader;
    /**
     * 循环显示的极限(缓存大小限制)
     */
    private final int MAX_READAHEAD_LIMIT = 4096;  
    public FileDisplayImpl(String filename) {
        this.filename = filename;
    }
    public void rawOpen() {
        try {
            reader = new BufferedReader(new FileReader(filename));
            reader.mark(MAX_READAHEAD_LIMIT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 装饰框
        System.out.println("=-=-=-=-=-= " + filename + " =-=-=-=-=-="); 
    }
    public void rawPrint() {
        try {
            String line;
            reader.reset(); // 回到mark的位置
            while ((line = reader.readLine()) != null) {
                System.out.println("> " + line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void rawClose() {
        // 装饰框
        System.out.println("=-=-=-=-=-= "); 
        try {
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【主类】

package com.atguigu.bridge.A2;

public class Main {
    public static void main(String[] args) {
        CountDisplay d = new CountDisplay(new FileDisplayImpl("star.txt"));
        d.multiDisplay(3);
    }
}

登场角色

  • Abstraction(抽象化):位于类的功能层次结构的最上层。它使用Implementor角色的方法定义了基本的功能。该角色中保存了Implementor 角色的实例
  • RefinedAbstraction(改善后的抽象化):在Abstraction角色的基础上增加了新功能
  • Implementor(实现者):位于“类的实现层次结构”的最上层。它定义了用于实现Abstraction 角色的接口(API)的方法
  • Concretelmplementor( 具体实现者):负责实现在Implementor 角色中定义的接口(API)

在这里插入图片描述

桥接模式在JDBC源码中的应用

在这里插入图片描述

总结

  • 实现了抽象和实现部分的分离,从而极大的提高了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统
  • 分离之后代码的扩展性更强,当想要增加功能时,只需要在“类的功能层次结构”一侧增加类即可,不必对“类的实现层次结构”做任何修改。而且,增加后的功能可以被“所有的实现”使用(例如,我们可以将“类的功能层次结构”应用于软件所运行的操作系统上。如果我们将某个程序中依赖于操作系统的部分划分为 Windows版、Macintosh 版、Unix 版,那么我们就可以用 Bridge模式中的“类的实现层次结构”来表现这些依赖于操作系统的部分。也就是说,我们需要编写一个定义这些操作系统的共同接口(API)的Implementor角色,然后编写Windows版、Macintosh版Unix版的3个Concretelmplementor角色。这样一来,无论在“类的功能层次结构”中增加多少个功能,它们都可以工作于这3个操作系统上。)
  • 对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成
  • 桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本
  • 桥接模式的引入增加了系统的理解和设计难度(较难分析出哪些是抽象层,哪些是实现层),由于聚合关联关系建立在抽象层要求开发者针对抽象进行设计和编程
  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性,即需要有这样的应用场景

文章说明

  • 本文章为本人学习尚硅谷的学习笔记,文章中大部分内容来源于尚硅谷视频(点击学习尚硅谷相关课程),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对尚硅谷的优质课程表示感谢。
  • 本人还同步阅读《图解设计模式》书籍(图解设计模式/(日)结城浩著;杨文轩译–北京:人民邮电出版社,2017.1),进而综合两者的内容,让知识点更加全面

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

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

相关文章

JDK9 接口特性(heima)

JDK9 接口特性&#xff08;heima&#xff09; 将log方法私有化

04. Idea集成Docker

目录 1、前言 2、开启docker远程 2.1、配置远程访问模式 2.2、重启docker服务 2.3、验证 3、Idea配置Docker 3.1、配置证书登录 4、创建Dockerfile 4.1、编写Dockerfile 4.2、配置启动项 4.3、访问接口 1、前言 上一节中&#xff0c;我们介绍了Dockerfile的方式构建…

MURF20100CT-ASEMI快恢复对管20A 1000V

编辑&#xff1a;ll MURF20100CT-ASEMI快恢复对管20A 1000V 型号&#xff1a;MURF20100CT 品牌&#xff1a;ASEMI 封装&#xff1a;TO-220F 恢复时间&#xff1a;50ns 正向电流&#xff1a;20A 反向耐压&#xff1a;1000V 芯片大小&#xff1a;102MIL*2 芯片个数&…

C语言每日一题:4.消失的数字+数字在升序数组中出现的次数+整数转换

消失的数字&#xff1a; 思路1&#xff1a;排序遍历 1.使用qsort排序数组判断当前数值1是否是数组下一个元素的数值。 2.如果是一直循环注意数组越界&#xff0c;如果不是那么当前的数组的数值1就是消失的数。 3.存在0——n的数字是第n个数没有了。循环过程中从头到尾也找不到这…

C++ 哈希的应用【位图】

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; C修行之路 &#x1f383;操作环境&#xff1a; Visual Studio 2022 版本 17.6.5 文章目录 &#x1f307;前言&#x1f3d9;️正文1、问题一2、位图概念3、位图的模拟实现3.1、基本思路3.2、set3.3、reset3.4、…

从电商指标洞察到运营归因,只需几句话?AI 数智助理准备好了!

Lily 是名入职不久的电商运营助理&#xff0c;最近她想要根据 2022 年的客单价情况&#xff0c;分析品牌 A 在不同电商渠道的用户行为和表现&#xff0c;并提供一些有价值的洞察和建议给客户。然而在向技术人员提报表需求后&#xff0c;技术人员以需求排满为借口拒绝了。 Lily …

STM32CUBEMX配置 定时器中断 和 上升沿中断(实现检测1s以内的脉冲个数)

———————————————————————————————————— ⏩ 大家好哇&#xff01;我是小光&#xff0c;嵌入式爱好者&#xff0c;一个想要成为系统架构师的大三学生。 ⏩最近在开发一个STM32H723ZGT6的板子&#xff0c;使用STM32CUBEMX做了很多驱动&#x…

速锐得开发社区-新一代汽车网络通信技术CAN FD的特点归纳

随着汽车工业的快速发展&#xff0c;汽车逐渐走向智能化&#xff0c;功能也越来越丰富&#xff0c;例如特斯拉、比亚迪、理想汽车为代表&#xff0c;在车载导航、驻车雷达、胎压监测、倒车影像、无钥匙启动、定速巡航、自动泊车、高级辅助驾驶系统、自动驾驶、域控制器、智能网…

慎投!中科院2区TOP期刊被标记为“On Hold”!

近日&#xff0c;小编从科睿唯安旗下的“Master Journal List”官网查到&#xff0c;知名老牌期刊Chemosphere竟然被标记为“On Hold”状态&#xff01;究竟是怎么回事呢&#xff1f; 中科院TOP期刊标记为“On Hold” 出版社&#xff1a;Elsevier ISSN&#xff1a;0045-6535 …

【广州华锐互动】无人值守变电站AR虚拟测控平台

无人值守变电站AR虚拟测控平台是一种基于增强现实技术的电力设备巡检系统&#xff0c;它可以利用增强现实技术将虚拟信息叠加在真实场景中&#xff0c;帮助巡检人员更加高效地完成巡检任务。这种系统的出现&#xff0c;不仅提高了巡检效率和准确性&#xff0c;还降低了巡检成本…

vray GPU渲染如何设置?最适合 VRay 渲染的 GPU 是什么?

Chaos 提供的 Vray GPU是一个独立的渲染引擎&#xff0c;提供 GPU 硬件加速。它还与 CPU&#xff08;处理器&#xff09;配合使用&#xff0c;并利用 CPU 和 GPU 硬件进行无缝混合渲染。 GPU 渲染使 Vray GPU 渲染引擎能够在系统的 GPU 而不是 CPU 上执行光线跟踪计算。由于 G…

Linux学习之case

case的格式如下&#xff1a; case "变量" in值1)指令集1;;值2)指令集2;;*)指令集n;; esaccase会将变量的值跟值1、值2等进行比较&#xff0c;符合就执行后边对应的指令集&#xff0c;注意)只是一个标识&#xff0c;表明这是一个分支&#xff0c;;;是一个分支结束&am…

Qt控件样式设置、QSS语法介绍

一、前言 QT是比较流行的跨平台应用程序框架,提供了丰富的控件库和功能强大的工具,用于创建各种类型的应用程序。虽然QT默认提供的样式看起来很不错,但是如果想要自定义的应用程序的外观,可以使用QT样式表(QSS)来设置控件的外观和行为。 QSS是一种基于CSS的样式表语言,…

DB-GPT:强强联合Langchain-Vicuna的应用实战开源项目,彻底改变与数据库的交互方式

今天看到 蚂蚁科技 Magic 开源的DB-GPT项目&#xff0c;觉得创意很好&#xff0c;集成了当前LLM的主流技术&#xff0c;主要如下 Langchain&#xff1a; 构建在LLM之上的应用开发框架HuggingFace: 模型标准&#xff0c;提供大模型管理功能Vicuna: 一个令GPT-4惊艳的开源聊天机…

odoo服务相关shell脚本

odoo服务相关shell脚本 1 确定系统不会自动休眠 ubuntu 22.04登陆之后&#xff0c;如果系统较长时间不操作&#xff0c;系统就自动休眠了。 如果重启之后&#xff0c;从来都没有登陆&#xff0c;就不会出现系统自动休眠的情况。 观察系统日志&#xff0c;发现类似如下的内容…

学好Elasticsearch系列-Mapping

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 文章目录 Mapping 的基本概念查看索引 Mapping 字段数据类型数字类型基本数据类型Keywords 类型Dates&#xff08;时间类型&#xff09;对象类型空间数据类型文档排名类型文本搜索类型 两种映射类型自动映射&…

视屏消音工具,支持指定词语,或者短句消音

这里写自定义目录标题 一个使用python开发的视频消音工具 一个使用python开发的视频消音工具 1.支持上传文件字幕,进行视屏整段语句进行静音处理 2.支持通过指定的词语或者句子进行精确的消音处理 3.支持输入多个词语,或者断句进行消音处理 页面比较简陋,但功能应该满足日常使…

Vue移动端项目--瑞幸咖啡重构优化

来了客官&#xff0c;好久不见&#xff01; 从年初开始&#xff0c;就有个想法&#xff0c;想着把之前做过的项目重新整理一下。毕竟今时不同往日&#xff0c;从现在的角度去看曾经做过的项目&#xff0c;倒是觉得有很多稚嫩的地方。毕竟无论做什么都是熟能生巧&#xff0c;由浅…

【干货分享】自动化测试理论知识以及相关框架

自动化测试 1.进行自动化测试的原因&#xff1a; a.黑盒测试回归测试效率低 b.手动测试的偶然性和不确定性 c.回归的覆盖率不足 d.交付的产品质量无法保证&#xff0c;全靠评估 e.系统越复杂&#xff0c;问题越多 f.上线时间长&#xff0c;构件失败率高导致的蝴蝶效应&#xf…

分享200+个关于AI的网站

分享200个关于AI的网站 欢迎大家访问&#xff1a;https://tools.haiyong.site/ai 快速导航 AI 应用AI 写作AI 编程AI 设计AI 作图AI 训练模型AI 影音编辑AI 效率助手 AI 应用 文心一言: https://yiyan.baidu.com/ 百度出品的人工智能语言模型 ChatGPT: https://chat.openai.c…