设计模式-桥接模式

news/2024/7/8 2:24:02

桥接模式( Bridge Pattern )也称为桥梁模式、接口(Interfce)模式或柄体( Handle and Body)模 式,是将抽象部分与它的具体实现部分分离,使它们都可以独立地变化,属于结构型模式。

原文:Decouple an abstraction from its implementation so that the two can vary independently.
翻译:解锅抽象和实现,使得两者可以独立的变化。
也可以理解成:一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。

桥接模式主要目的是通过组合的方式建立两个类之间的联系,而不是继承。但又类似于多重继承方 案,但是多重继承方案往往违背了类得单一职责原则,其复用性比较差,桥接模式是比多重继承更好的 替代方案。桥接模式的核心在于解耦抽象和实现。
注:此处的抽象并不是指抽象类象接口这种商层概念,实现也不是继承或接口实现。抽象与实现其实指的是两种独立 变化的维度。

桥接模式的UML类图

具体代码:

public abstract class Abstraction {
    private final Implementor implementor;
    public Abstraction(Implementor implementor){
        this.implementor = implementor;
    }

    public void function(){
        implementor.implementation();
    }
}
复制代码
public class RefinedAbstraction extends Abstraction{
    public RefinedAbstraction(Implementor implementor) {
        super(implementor);
    }

    public void refinedFunction(){
        //子类扩展方法
        super.implementor.implementation();
    }

}
复制代码
public interface Implementor {
    void implementation();
}
复制代码
public class ConcreteImplementorA implements Implementor{
    @Override
    public void implementation() {
        System.out.println("ConcreteImplementorA");
    }
}
复制代码
public class ConcreteImplementorB implements Implementor{
    @Override
    public void implementation() {
        System.out.println("ConcreteImplementorB");
    }
}
复制代码
public class Test {
    public static void main(String[] args) {
        Implementor implementor = new ConcreteImplementorA();
        RefinedAbstraction refinedAbstraction = new RefinedAbstraction(implementor);
        refinedAbstraction.refinedFunction();
    }
}
复制代码

示例代码

举个例子,家用电器,有各种家用电器比如洗衣机、冰箱、空调,所有的家用电器又对应这自己的品牌,这就分离出来了两个维度一个是家用电器,还一个是品牌两个都能扩展,这里就很适合使用桥接模式 具体代码如下: 首先把家用电器抽象出来

public abstract class Product {
    protected ICompany company;


    public Product(ICompany company) {
        this.company = company;
    }

    public abstract void printProductInfo();
}
复制代码

具体实现

public class AirConditioner extends Product{
    public AirConditioner(ICompany company) {
        super(company);
    }
    @Override
    public void printProductInfo() {
        System.out.print(company.getName());
        System.out.print("公司---");
        System.out.println("空调");
    }
}
复制代码
public class Washer extends Product{
    public Washer(ICompany company) {
        super(company);
    }
    @Override
    public void printProductInfo() {
        System.out.print(company.getName());
        System.out.print("公司---");
        System.out.println("洗衣机");
    }
}
复制代码

然后就是公司的接口

public interface ICompany {
    String getName();
}
复制代码

具体实现:

public class Haier implements ICompany{
    @Override
    public String getName() {
        return "海尔";
    }
}
复制代码
public class Meidi implements ICompany{
    @Override
    public String getName() {
        return "美的";
    }
}
复制代码

最后使用方式:

public class Test {
    public static void main(String[] args) {
        Product product = new Washer(new Haier());
        Product product1 = new Washer(new Meidi());
        product.printProductInfo();
        product1.printProductInfo();
    }
}
复制代码

在源码中的应用

大家非常熟悉的 JDBC API,其中有一个Driver类就是桥接对象。我们都知道,我们在使用的时候 通过Class.forName()方法可以动态加载各个数据库厂商实现的 Driver 类。具体客户端应用代码如下, 以 MySQL的实现为例

Class.forName("com.mysql.jdbc.Driver");
Connection conn= DriverManager.getConnection("jdbc:mysql://1ocalhost:3306/test","root","root");
Statement stmt = conn.createStatement();
ResultSet resultSet = stmt.executeQuery("select *from table");
复制代码

先看Driver这个类

public interface Driver {
    Connection connect(String url, java.util.Properties info)
        throws SQLException;
    boolean acceptsURL(String url) throws SQLException;
    DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info)
                         throws SQLException;
    int getMinorVersion();
    boolean jdbcCompliant();
    public Logger getParentLogger() throws SQLFeatureNotSupportedException;
}
复制代码

发现SDK提供的Driver 类是一个接口

mysql实现了这个接口 可以看到当加载Driver这个类的时候会执行

DriverManager.registerDriver(new Driver());
复制代码
public static void registerDriver(java.sql.Driver driver)
    throws SQLException {

    registerDriver(driver, null);
}
复制代码
public static void registerDriver(java.sql.Driver driver,
        DriverAction da)
    throws SQLException {

    /* Register the driver if it has not already been added to our list */
    if (driver != null) {
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    } else {
        // This is for compatibility with the original DriverManager
        throw new NullPointerException();
    }

    println("registerDriver: " + driver);

}
复制代码

JDK底层是把mysql 的Driver的实例包装成了 DriverInfo对象.

接下来再看 getConnection方法

@CallerSensitive
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()));
}
复制代码
private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        /*
         * When callerCl is null, we should check the application's
         * (which is invoking this class indirectly)
         * classloader, so that the JDBC driver class outside rt.jar
         * can be loaded from here.
         */
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        if (callerCL == null || callerCL == ClassLoader.getPlatformClassLoader()) {
            callerCL = Thread.currentThread().getContextClassLoader();
        }

        if (url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }

        println("DriverManager.getConnection("" + url + "")");

        ensureDriversInitialized();

        // Walk through the loaded registeredDrivers attempting to make a connection.
        // Remember the first exception that gets raised so we can reraise it.
        SQLException reason = null;

        for (DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if (isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }

            } else {
                println("    skipping: " + aDriver.driver.getClass().getName());
            }

        }

        // if we got here nobody could connect.
        if (reason != null)    {
            println("getConnection failed: " + reason);
            throw reason;
        }

        println("getConnection: no suitable driver found for "+ url);
        throw new SQLException("No suitable driver found for "+ url, "08001");
    }


}
复制代码

可以看到这个 Connection就是调用的额mysql实现的 driver类返回的。

所以真实操作数据库的代码都是各个厂商根据自己数据库生成的,jdk只是统一了接口,所以切换数据库只需要切换驱动就行了。

这其中 DriverManager就相当于 Abstraction角色 Driver就是 Implementor角色,每个厂商对Driver的实现就是ConcreteImplementor角色

桥接模式优缺点

桥接(Bridge)模式的定义是:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
通过上面的讲解,我们能很好的感觉到桥接模式遵循了里氏替换原则和依赖倒置原则,最终实现了开闭原则,对修改关闭,对扩展开放。这里将桥接模式的优缺点总结如下:

优点:

  • 抽象与实现分离,扩展能力强
  • 符合开闭原则
  • 符合合成复用原则
  • 其实现细节对客户透明

缺点:

  • 增加了系统的理解与设计难度
  • 需要正确地识别系统中两个独立变化的维度

http://lihuaxi.xjx100.cn/news/298843.html

相关文章

提到Canvas,必须好好唠唠它的图像操作能力

前情提要 接续一下之前对Canvas的探索。本篇分享一下对图像操作的阅读和研究。 日常开发中&#xff0c;时常遇到对图像的处理的场景。精美的图像做为背景或者场景&#xff0c;相对会吸引人。 Canvas图像API十分强大。可以通过Canvas图像API加载图像数据&#xff0c;进行裁剪…

安装GCC教程

安装GCC-9.3.0全指导 一.安装准备 1_1下载GCC-9.3.0安装包* 手动下载辅助包&#xff0c;自动下载太慢&#xff0c;咱直接自己动手解决 ps&#xff1a;下载源精选国内清华源&#xff0c;速度无敌 wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-9.3.0/gcc-9.3.0.tar…

Android -- 每日一问:能讲讲 Android 的 Handler 机制吗?

典型回答 讲清楚Android中的消息机制&#xff0c;先表述一下和Handler相关的一些类&#xff1a; Message&#xff1a;消息分为硬件产生的消息(如按钮、触摸)和软件生成的消息&#xff1b; MessageQueue&#xff1a;消息队列的主要功能向消息池投递消息(MessageQueue.enqueueMe…

Docker基础组件、安装启动和Docker生命周期

Docker安装部署 Docker引擎 运行镜像生成容器。应用程序跑在容器中 Docker Daemon 安装使用Docker&#xff0c;得先运行Docker Daemon进程&#xff0c;用于管理Docker&#xff0c;如&#xff1a; 镜像 images容器 containers网络 network数据卷 Data Volumes Rest接口 提…

传奇架设教程,五分钟学会传奇单机架设,打造属于自己的热血传奇

传奇单机架设教程,五分钟完成单机架设 准备工具:DBC2000 版本 补丁 传奇架设步骤 1.解压补丁到传奇客户端 2.解压版本到D盘盘符 3.安装配置DBC2000 4.打开引擎控制器启动 5.打开登陆器选择单机127.0.0.1 确保自己的热血传奇客户端是16年以后的 最好是最新版的…

ouster-32激光雷达使用---ROS驱动编译与使用

ouster-32激光雷达使用---ROS驱动编译与使用驱动安装依赖下载编译功能包功能连接到实时传感器重播录制的包录制新的包文件此 ROS 软件包支持所有固件 v2.0 或更高版本的 Ouster 传感器。启动后&#xff0c;驱动程序将配置并连接到选定的传感器设备&#xff0c;连接后&#xff0…

【小程序】导航栏和内容页面联动效果实现

&#x1f4ad;&#x1f4ad; ✨&#xff1a; 微信小程序导航栏和页面联动   &#x1f49f;&#xff1a;东非不开森的主页   &#x1f49c;: 因为很多东西来不及去做去看可是时间很快总是赶不上&#xff0c;所以要去成长呀&#x1f49c;&#x1f49c;   &#x1f338;: 如有错…

基于Δ-Σ模数转换器的梳状滤波器的设计与matlab仿真

欢迎订阅《FPGA学习入门100例教程》、《MATLAB学习入门100例教程》 目录 一、理论基础 二、核心程序 三、测试结果 一、理论基础 Δ-Σ模数转换器是一种低速&#xff0c;高精度的过采样模数转换器广泛的应用于信号采集和处理、数字通信、自动检测、自动控制和多媒体技术等领…