• 设计模式-桥接模式


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

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

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

    桥接模式的UML类图

    具体代码:

    1. public abstract class Abstraction {
    2. private final Implementor implementor;
    3. public Abstraction(Implementor implementor){
    4. this.implementor = implementor;
    5. }
    6. public void function(){
    7. implementor.implementation();
    8. }
    9. }
    10. 复制代码
    1. public class RefinedAbstraction extends Abstraction{
    2. public RefinedAbstraction(Implementor implementor) {
    3. super(implementor);
    4. }
    5. public void refinedFunction(){
    6. //子类扩展方法
    7. super.implementor.implementation();
    8. }
    9. }
    10. 复制代码
    1. public interface Implementor {
    2. void implementation();
    3. }
    4. 复制代码
    1. public class ConcreteImplementorA implements Implementor{
    2. @Override
    3. public void implementation() {
    4. System.out.println("ConcreteImplementorA");
    5. }
    6. }
    7. 复制代码
    1. public class ConcreteImplementorB implements Implementor{
    2. @Override
    3. public void implementation() {
    4. System.out.println("ConcreteImplementorB");
    5. }
    6. }
    7. 复制代码
    1. public class Test {
    2. public static void main(String[] args) {
    3. Implementor implementor = new ConcreteImplementorA();
    4. RefinedAbstraction refinedAbstraction = new RefinedAbstraction(implementor);
    5. refinedAbstraction.refinedFunction();
    6. }
    7. }
    8. 复制代码

    示例代码

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

    1. public abstract class Product {
    2. protected ICompany company;
    3. public Product(ICompany company) {
    4. this.company = company;
    5. }
    6. public abstract void printProductInfo();
    7. }
    8. 复制代码

    具体实现

    1. public class AirConditioner extends Product{
    2. public AirConditioner(ICompany company) {
    3. super(company);
    4. }
    5. @Override
    6. public void printProductInfo() {
    7. System.out.print(company.getName());
    8. System.out.print("公司---");
    9. System.out.println("空调");
    10. }
    11. }
    12. 复制代码
    1. public class Washer extends Product{
    2. public Washer(ICompany company) {
    3. super(company);
    4. }
    5. @Override
    6. public void printProductInfo() {
    7. System.out.print(company.getName());
    8. System.out.print("公司---");
    9. System.out.println("洗衣机");
    10. }
    11. }
    12. 复制代码

    然后就是公司的接口

    1. public interface ICompany {
    2. String getName();
    3. }
    4. 复制代码

    具体实现:

    1. public class Haier implements ICompany{
    2. @Override
    3. public String getName() {
    4. return "海尔";
    5. }
    6. }
    7. 复制代码
    1. public class Meidi implements ICompany{
    2. @Override
    3. public String getName() {
    4. return "美的";
    5. }
    6. }
    7. 复制代码

    最后使用方式:

    1. public class Test {
    2. public static void main(String[] args) {
    3. Product product = new Washer(new Haier());
    4. Product product1 = new Washer(new Meidi());
    5. product.printProductInfo();
    6. product1.printProductInfo();
    7. }
    8. }
    9. 复制代码

    在源码中的应用

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

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

    先看Driver这个类

    1. public interface Driver {
    2. Connection connect(String url, java.util.Properties info)
    3. throws SQLException;
    4. boolean acceptsURL(String url) throws SQLException;
    5. DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info)
    6. throws SQLException;
    7. int getMinorVersion();
    8. boolean jdbcCompliant();
    9. public Logger getParentLogger() throws SQLFeatureNotSupportedException;
    10. }
    11. 复制代码

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

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

    1. DriverManager.registerDriver(new Driver());
    2. 复制代码
    1. public static void registerDriver(java.sql.Driver driver)
    2. throws SQLException {
    3. registerDriver(driver, null);
    4. }
    5. 复制代码
    1. public static void registerDriver(java.sql.Driver driver,
    2. DriverAction da)
    3. throws SQLException {
    4. /* Register the driver if it has not already been added to our list */
    5. if (driver != null) {
    6. registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    7. } else {
    8. // This is for compatibility with the original DriverManager
    9. throw new NullPointerException();
    10. }
    11. println("registerDriver: " + driver);
    12. }
    13. 复制代码

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

    接下来再看 getConnection方法

    1. @CallerSensitive
    2. public static Connection getConnection(String url,
    3. String user, String password) throws SQLException {
    4. java.util.Properties info = new java.util.Properties();
    5. if (user != null) {
    6. info.put("user", user);
    7. }
    8. if (password != null) {
    9. info.put("password", password);
    10. }
    11. return (getConnection(url, info, Reflection.getCallerClass()));
    12. }
    13. 复制代码
    1. private static Connection getConnection(
    2. String url, java.util.Properties info, Class caller) throws SQLException {
    3. /*
    4. * When callerCl is null, we should check the application's
    5. * (which is invoking this class indirectly)
    6. * classloader, so that the JDBC driver class outside rt.jar
    7. * can be loaded from here.
    8. */
    9. ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
    10. if (callerCL == null || callerCL == ClassLoader.getPlatformClassLoader()) {
    11. callerCL = Thread.currentThread().getContextClassLoader();
    12. }
    13. if (url == null) {
    14. throw new SQLException("The url cannot be null", "08001");
    15. }
    16. println("DriverManager.getConnection("" + url + "")");
    17. ensureDriversInitialized();
    18. // Walk through the loaded registeredDrivers attempting to make a connection.
    19. // Remember the first exception that gets raised so we can reraise it.
    20. SQLException reason = null;
    21. for (DriverInfo aDriver : registeredDrivers) {
    22. // If the caller does not have permission to load the driver then
    23. // skip it.
    24. if (isDriverAllowed(aDriver.driver, callerCL)) {
    25. try {
    26. println(" trying " + aDriver.driver.getClass().getName());
    27. Connection con = aDriver.driver.connect(url, info);
    28. if (con != null) {
    29. // Success!
    30. println("getConnection returning " + aDriver.driver.getClass().getName());
    31. return (con);
    32. }
    33. } catch (SQLException ex) {
    34. if (reason == null) {
    35. reason = ex;
    36. }
    37. }
    38. } else {
    39. println(" skipping: " + aDriver.driver.getClass().getName());
    40. }
    41. }
    42. // if we got here nobody could connect.
    43. if (reason != null) {
    44. println("getConnection failed: " + reason);
    45. throw reason;
    46. }
    47. println("getConnection: no suitable driver found for "+ url);
    48. throw new SQLException("No suitable driver found for "+ url, "08001");
    49. }
    50. }
    51. 复制代码

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

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

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

    桥接模式优缺点

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

    优点:

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

    缺点:

    • 增加了系统的理解与设计难度
    • 需要正确地识别系统中两个独立变化的维度
  • 相关阅读:
    NX二次开发-使用NXOpen::DisplayModification类,将UF曲线-面-体等tag设置颜色
    卷积神经网络工作原理的直观理解
    爱尔眼科角膜塑形镜验配超百万,全力做好“角塑镜把关人”
    12月1日(第三天)
    【算法学习】-【双指针】-【有效三角形的个数】
    同城跑腿微信小程序源码系统完整搭建教程
    【Spring Cloud】网关Gateway的请求过滤工厂RequestRateLimiterGatewayFilterFactory
    艺术VQA的数据集与基线baselines
    Airtest新手升级:一个相对完整的纯.py脚本是怎样子的
    Leetcode1475. Final Prices With a Special Discount in a Shop (单调栈经典题)
  • 原文地址:https://blog.csdn.net/BASK2311/article/details/128143923