【设计模式】9.桥接模式

news2024/9/23 15:29:34

概述

现在有一个需求,需要创建不同的图形,并且每个图形都有可能会有不同的颜色。我们可以利用继承的方式来设计类的关系:
image.png

我们可以发现有很多的类,假如我们再增加一个形状或再增加一种颜色,就需要创建更多的类。

试想,在一个有多种可能会变化的维度的系统中,用继承方式会造成类爆炸,扩展起来不灵活。每次在一个维度上新增一个具体实现都要增加多个子类。为了更加灵活的设计系统,我们此时可以考虑使用桥接模式。

定义:

​ 将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。

结构

桥接(Bridge)模式包含以下主要角色:

  • 抽象化(Abstraction)角色 :定义抽象类,并包含一个对实现化对象的引用。
  • 扩展抽象化(Refined Abstraction)角色 :是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
  • 实现化(Implementor)角色 :定义实现化角色的接口,供扩展抽象化角色调用。
  • 具体实现化(Concrete Implementor)角色 :给出实现化角色接口的具体实现。

案例

【例】视频播放器

需要开发一个跨平台视频播放器,可以在不同操作系统平台(如Windows、Mac、Linux等)上播放多种格式的视频文件,常见的视频格式包括RMVB、AVI、WMV等。该播放器包含了两个维度,适合使用桥接模式。

类图如下:

image.png

代码如下:

//视频文件
public interface VideoFile {
    void decode(String fileName);
}

//avi文件
public class AVIFile implements VideoFile {
    public void decode(String fileName) {
        System.out.println("avi视频文件:"+ fileName);
    }
}

//rmvb文件
public class REVBBFile implements VideoFile {

    public void decode(String fileName) {
        System.out.println("rmvb文件:" + fileName);
    }
}

//操作系统版本
public abstract class OperatingSystemVersion {

    protected VideoFile videoFile;

    public OperatingSystemVersion(VideoFile videoFile) {
        this.videoFile = videoFile;
    }

    public abstract void play(String fileName);
}

//Windows版本
public class Windows extends OperatingSystem {

    public Windows(VideoFile videoFile) {
        super(videoFile);
    }

    public void play(String fileName) {
        videoFile.decode(fileName);
    }
}

//mac版本
public class Mac extends OperatingSystemVersion {

    public Mac(VideoFile videoFile) {
        super(videoFile);
    }

    public void play(String fileName) {
		videoFile.decode(fileName);
    }
}

//测试类
public class Client {
    public static void main(String[] args) {
        OperatingSystem os = new Windows(new AVIFile());
        os.play("战狼3");
    }
}

好处:

  • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。

    如:如果现在还有一种视频文件类型wmv,我们只需要再定义一个类实现VideoFile接口即可,其他类不需要发生变化。

  • 实现细节对客户透明

使用场景

  • 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。
  • 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
  • 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。

JDBC

我们通过 JDBC 驱动的例子来解释一下。JDBC 驱动是桥接模式的经典应用。我们先来看一下,如何利用 JDBC 驱动来查询数据库。具体的代码如下所示:

Class.forName("com.mysql.jdbc.Driver"); // 加载及注册JDBC驱动程序
String url = "jdbc:mysql://localhost:3306/sample_db?user=root&password=your_password";
Connection con = DriverManager.getConnection(url);
Statement stmt = con.createStatement();
String query = "select * from test";
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
  rs.getString(1);
  rs.getInt(2);
}
  1. 如果我们想要把 MySQL 数据库换成 Oracle 数据库,只需要把第一行代码中的 com.mysql.jdbc.Driver 换成 oracle.jdbc.driver.OracleDriver 就可以了。当然,也有更灵活的实现方式,我们可以把需要加载的 Driver 类写到配置文件中,当程序启动的时候,自动从配置文件中加载,这样在切换数据库的时候,我们都不需要修改代码,只需要修改配置文件就可以了。
  2. 不管是改代码还是改配置,在项目中,从一个数据库切换到另一种数据库,都只需要改动很少的代码,或者完全不需要改动代码,那如此优雅的数据库切换是如何实现的呢?
  3. 源码之下无秘密。要弄清楚这个问题,我们先从 com.mysql.jdbc.Driver 这个类的代码看起。我摘抄了部分相关代码,放到了这里,你可以看一下。
package com.mysql.jdbc;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
  static {
    try {
      java.sql.DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
      throw new RuntimeException("Can't register driver!");
    }
  }

  /**
   * Construct a new driver and register it with DriverManager
   * @throws SQLException if a database error occurs.
   */
  public Driver() throws SQLException {
  // Required for Class.forName().newInstance()
  }
}

结合 com.mysql.jdbc.Driver 的代码实现,我们可以发现,当执行 Class.forName(“com.mysql.jdbc.Driver”) 这条语句的时候,实际上是做了两件事情。第一件事情是要求 JVM 查找并加载指定的 Driver 类,第二件事情是执行该类的静态代码,也就是将 MySQL Driver 注册到 DriverManager 类中。

现在,我们再来看一下,DriverManager 类是干什么用的。具体的代码如下所示。当我们把具体的 Driver 实现类(比如,com.mysql.jdbc.Driver)注册到 DriverManager 之后,后续所有对 JDBC 接口的调用,都会委派到对具体的 Driver 实现类来执行。而 Driver 实现类都实现了相同的接口(java.sql.Driver ),这也是可以灵活切换 Driver 的原因。

public class DriverManager {
  private static final  CopyOnWriteArrayList<DriverInfo> registeredDrivers =  = new CopyOnWriteArrayList();

  // ...
  static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
  }

  // ...
  public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException {
    if (driver != null) {
      registeredDrivers.addIfAbsent(new DriverInfo(driver));
    } else {
      throw new NullPointerException();
    }
  }

  public static Connection getConnection(String url, String user, String password)
      throws SQLException {
    java.util.Properties info = new java.util.Properties();
    if (user != null) {
      info.put("user", user);
    }
    if (password != null) {
      info.put("password", password);
    }

    return (getConnection(url, info, Reflection.getCallerClass()));
  }
  // ...
}

桥接模式的定义是“将抽象和实现解耦,让它们可以独立变化”。那弄懂定义中“抽象”和“实现”两个概念,就是理解桥接模式的关键。那在 JDBC 这个例子中,什么是“抽象”?什么是“实现”呢?

实际上,JDBC 本身就相当于“抽象”。注意,这里所说的“抽象”,指的并非“抽象类”或“接口”,而是跟具体的数据库无关的、被抽象出来的一套“类库”。具体的 Driver(比如,com.mysql.jdbc.Driver)就相当于“实现”。注意,这里所说的“实现”,也并非指“接口的实现类”,而是跟具体数据库相关的一套“类库”。JDBC 和 Driver 独立开发,通过对象之间的组合关系,组装在一起。JDBC 的所有逻辑操作,最终都委托给 Driver 来执行。

img

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

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

相关文章

redis 05 篇——主从复制

redis 05 篇——主从复制1. 前言1.1 什么是复制&#xff1f;1.1.1 复制概述1.1.2 主从复制的架构1.2 为什么要使用主从复制&#xff1f;1.3 主从复制主要的命令配置2. 准备工作3. 核心配置3.1 主服务器3.2 从服务器4. 实例演示4.1 简单实例——两台服务器4.1.1 同一服务多个red…

小程序:使用分包异步化解决一个分包引入另一个分包的组件/函数的问题

背景 我们一般使用小程序插件的时候&#xff0c;喜欢将其放在分包中&#xff0c;因为插件体积会打包进主包内&#xff0c;很容易造成主包体积超过 2M 从而无法发布&#xff0c;我们暂且叫这个有插件的分包叫分包P&#xff0c;这时候另外两个业务分包XY&#xff0c;想引入这个分…

从Java培训班出来好找工作吗?

个人觉得这个问题要从两方面来看&#xff0c;首先是培训班的Java课程质量如何&#xff0c;是否贴合用人单位实际需求&#xff0c;学出来的技术能对口&#xff1b;其次是培训班是否保障就业&#xff0c;有就业机会渠道推荐&#xff0c;比如老学员内推、合作企业人才输送以及企业…

【Spring Cloud Alibaba】008-Sentinel

【Spring Cloud Alibaba】008-Sentinel 文章目录【Spring Cloud Alibaba】008-Sentinel一、服务雪崩1、概述2、解决方案常见的容错机制二、Sentinel&#xff1a;分布式系统的流量防卫兵1、**Sentinel** 概述简介特性Sentinel 的开源生态Sentinel 的历史2、Sentinel 基本概念资源…

低代码系统能够解决哪些痛点?

低代码系统能够解决哪些痛点&#xff1f;如果用4句话去归纳&#xff0c;低代码开发可以解决以下问题—— 为企业提供更高的灵活性&#xff0c;用户可以突破代码的限制自主开发业务应用&#xff1b;通过减少对专业软件开发人员的依赖&#xff0c;公司可以快速响应市场上的新业务…

cartographer中分支定界法理解——为什么能保证上界

定界原理 储存的不同分辨率栅格图&#xff0c;为保证上边界正确性&#xff0c;即高层中的评分一定高于其底层节点的评分&#xff0c;压缩的地图并非直接从原图固定间隔采样&#xff0c;而是将固定间隔中所有坐标概率值最大值作为低分辨率地图。 理解&#xff1a; 1、其在计算…

ESP32中MQTT通讯

MQTT文档介绍 一、在PC上可以使用 MQTT X 工具&#xff1a;&#xff08;参考地址&#xff09; 1、客户端下载&#xff1a;MQTT X 工具下载地址 2、EMQX服务器下载地址 3.打开命令行工具&#xff0c;进入目录运行EMQX服务。 电脑左下角&#xff0c;右键开始->运行->输…

“双碳”目标下二氧化碳地质封存技术应用前景及模型构建

我国二氧化碳地质封存技术起步较晚&#xff0c;目前仍没有一套相对完整的行业规范&#xff1b;且就该技术而言&#xff0c;涉及环节众多&#xff0c;理论相对复杂&#xff0c;对于行业的新入局者不太友好。因此&#xff0c;结合时代背景&#xff0c;我们首次尝试对二氧化碳地质…

升级多语言,应用国际化_三叠云

国际化配置 路径 表单设计 >> 字段属性 功能简介 【字段】新增 「国际化配置」 这是一个和国际化配合的功能&#xff0c;可以在用户通过切换不同的语言环境时&#xff0c;表单的标题、提示语等将会根据相应的语言环境切换到相应的表单字段标题、提示语&#xff0c;使…

Eureka注册中心-Ribbon负载均衡

1、Eureka注册中心服务注册与发现提供了一个服务注册中心、服务发现的客户端&#xff0c;还有一个方便查看所有注册的服务的界面。所有的服务使用Eureka的服务发现客户端来将自己注册到Eureka的服务器上。1.1 、Eureka的结构和作用1.1.1、Eureka的作用1.1.2、获取地址信息的流程…

【C++进阶】三、二叉搜索树

目录 一、二叉搜索树 1.1 概念 1.2 二叉搜索树操作 二、二叉搜索树实现 2.1 框架总览 2.2 实现接口总览 2.2.1 构造函数 2.2.2 拷贝构造 2.2.3 赋值重载 2.2.4 析构函数 2.2.5 二叉搜索树的遍历 2.2.6 插入函数 2.2.7 查找函数 2.2.8 删除函数 2.3 二叉搜索数完整…

MotionLayout动画效果实现的几种方式

前言MotionLayout 的使用大家应该都会了&#xff0c;如果不会看这里。本文就不科普如何使用&#xff0c;什么属性是什么意思&#xff0c;怎么使用之类的了&#xff0c;这里只是探讨一下 MotionLayout 效果实现的几种方式。一、ConstraintLayout 的方式定义我们知道 MotionLayou…

第三回:布局格式定方圆

import numpy as np import pandas as pd import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] #用来正常显示中文标签 plt.rcParams[axes.unicode_minus] False #用来正常显示负号一、子图 1. 使用 plt.subplots 绘制均匀状态下的子图 返回元素分…

ROS1学习笔记:ROS中的坐标管理系统(ubuntu20.04)

参考B站古月居ROS入门21讲&#xff1a;ROS中的坐标系管理系统 基于VMware Ubuntu 20.04 Noetic版本的环境 文章目录一、机器人中的坐标变换二、TF功能包三、小海龟跟随实验3.1 启动实验3.2 查看当前的TF树3.3 坐标相对位置可视化3.3.1 tf_echo3.3.2 rviz一、机器人中的坐标变换…

2023上半年软考,广州/东莞/深圳/江苏报班是明智的选择

软考是全国计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff08;简称软考&#xff09;项目&#xff0c;是由国家人力资源和社会保障部、工业和信息化部共同组织的国家级考试&#xff0c;既属于国家职业资格考试&#xff0c;又是职称资格考试。 系统集成…

飞桨paddlenlp安装报错python setup.py egg_info did not run successfully.

原因缺少setuptools_scm 安装setuptools_scm pip install setuptools_scm 然后再安装paddlenlp就会噼里啪啦的下载一大堆东东了 下载OVER

【K8S系列】第十四讲:初识K8s架构之服务器的变迁

目录 序言 1简介 1.1 物理机时代 物理服务器的缺点 1.2 虚拟机 虚拟机优缺点 1.3 容器时代 1.4 总结 2 容器编排 2.1 什么是容器编排 序言 人生犹如一股奔流&#xff0c;没有暗礁&#xff0c;激不起美丽的浪花。 三言两语&#xff0c;不如细心探索 今天学习一下K8s…

ROS从入门到精通2-6:Rviz可视化进阶(画坐标轴、直线、平面、圆柱等)

目录0 专栏介绍1 Rviz可视化2 环境配置3 使用方法4 测试用例0 专栏介绍 本专栏旨在通过对ROS的系统学习&#xff0c;掌握ROS底层基本分布式原理&#xff0c;并具有机器人建模和应用ROS进行实际项目的开发和调试的工程能力。 &#x1f680;详情&#xff1a;《ROS从入门到精通》…

因《狂飙》张颂文,吐槽广东人比内地人聪明,周杰才是地域黑鼻祖

最近一段时间&#xff0c;电视剧《狂飙》落下帷幕&#xff0c;但是关于这部电视剧的争议&#xff0c;却从来就没有停止过。尤其是主演张颂文&#xff0c;更是受到了大家的关注&#xff0c;不但圈住了很多的粉丝&#xff0c;影视圈很多同行也对他赞许有加。 自古以来同行是冤家&…

SSM-SpringBoot(快速启动,yaml配置)

1 SpingBoot简介 1.1 创建SpringBoot项目 创建 层次 boot程序最基本的架子 开发 RestController RequestMapping("/books") public class BookController {GetMapping("/{id}")public String getById(PathVariable Integer id){System.out.println(&qu…