设计模式-桥接模式
创始人
2024-03-14 04:33:08
0

桥接模式( 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{@Overridepublic void implementation() {System.out.println("ConcreteImplementorA");}
}
复制代码
public class ConcreteImplementorB implements Implementor{@Overridepublic 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);}@Overridepublic void printProductInfo() {System.out.print(company.getName());System.out.print("公司---");System.out.println("空调");}
}
复制代码
public class Washer extends Product{public Washer(ICompany company) {super(company);}@Overridepublic void printProductInfo() {System.out.print(company.getName());System.out.print("公司---");System.out.println("洗衣机");}
}
复制代码

然后就是公司的接口

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

具体实现:

public class Haier implements ICompany{@Overridepublic String getName() {return "海尔";}
}
复制代码
public class Meidi implements ICompany{@Overridepublic 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 DriverManagerthrow 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)模式的定义是:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
通过上面的讲解,我们能很好的感觉到桥接模式遵循了里氏替换原则和依赖倒置原则,最终实现了开闭原则,对修改关闭,对扩展开放。这里将桥接模式的优缺点总结如下:

优点:

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

缺点:

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

相关内容

热门资讯

汽车油箱结构是什么(汽车油箱结... 本篇文章极速百科给大家谈谈汽车油箱结构是什么,以及汽车油箱结构原理图解对应的知识点,希望对各位有所帮...
美国2年期国债收益率上涨15个... 原标题:美国2年期国债收益率上涨15个基点 美国2年期国债收益率上涨15个基...
嵌入式 ADC使用手册完整版 ... 嵌入式 ADC使用手册完整版 (188977万字)💜&#...
重大消息战皇大厅开挂是真的吗... 您好:战皇大厅这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...
盘点十款牵手跑胡子为什么一直... 您好:牵手跑胡子这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游...
senator香烟多少一盒(s... 今天给各位分享senator香烟多少一盒的知识,其中也会对sevebstars香烟进行解释,如果能碰...
终于懂了新荣耀斗牛真的有挂吗... 您好:新荣耀斗牛这款游戏可以开挂,确实是有挂的,需要了解加客服微信8435338】很多玩家在这款游戏...
盘点十款明星麻将到底有没有挂... 您好:明星麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【5848499】很多玩家在这款游戏...
总结文章“新道游棋牌有透视挂吗... 您好:新道游棋牌这款游戏可以开挂,确实是有挂的,需要了解加客服微信【7682267】很多玩家在这款游...
终于懂了手机麻将到底有没有挂... 您好:手机麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...