设计模式-桥接模式

桥接模式

定义

抽象部分和具体实现部分分离

  • 让他们可以独立的变化
  • 通过组合的方式建立两个类之间的关系而不是继承

结构型模式

生活中的场景

连接了两个维度的东西

网络连接

桥接模式

虚拟网卡和物理网卡连在一起

通用的写法

image-20210105193129521

里面有几个关键角色

Abstraction——抽象化角色

​ 它的主要职责是定义出该角色的行为,同时保存一个对实现化角色的引用,该角色一般是抽象类。

Implementor——实现化角色

它是接口或者抽象类,定义角色必需的行为和属性。

RefinedAbstraction——修正抽象化角色

它引用实现化角色对抽象化角色进行修正。

ConcreteImplementor——具体实现化角色

它实现接口或抽象类定义的方法和属性。

public class RefinedAbstraction extends Abstraction {
  public RefinedAbstraction(IImplementor implementor) {
    super(implementor);
  }

  @Override
  public void operation() {
    super.operation();
    System.out.println("refined operation");
  }
}
public class Test {

  public static void main(String[] args) { 
    //定义一个实现化角色
    Implementor imp = new ConcreteImplementor1();
    //定义一个抽象化角色
    Abstraction abs = new RefinedAbstraction(imp); 
    //执行行文
    abs.request(); 
  }

}
}

测试用例

public class Test {
  public static void main(String[] args) {
    // 来一个实现化角色
    IImplementor imp = new ConcreteImplementorA();
    // 来一个抽象化角色,聚合实现
    Abstraction abs = new RefinedAbstraction(imp);
    // 执行操作
    abs.operation();
  }
}

​ 各位可能要问,为什么要增加一个构造函数?答案是为了提醒子类,你必须做这项工作,指定实现者,特别是已经明确了实现者,则尽量清晰明确地定义出来。

案例

抽象工厂的问题

以前我们在写抽象工厂模式的时候是这样来做的

image-20201230192218653

从上图可以看到,抽象工厂是用来帮我们创建不同的课程有相同的共性的问题

例如Java课程和python课程都需要记笔记和录制视频,所以我们可以通过抽象工厂CourseFactory来创建,但是这种方式是通过继承来做的,并且我们的产品和工厂都绑的太死了,所以我们可以通过桥梁模式来帮我们解绑,用组合和聚合来代理继承。

桥接模式来松绑

image-20210105193952831

可以看到通过AbstractCourse给笔记和视频搭建了一个桥梁

public class AbstractCourse implements ICourse {
  private INote note;
  private IVideo video;

  public void setNote(INote note) {
    this.note = note;
  }

  public void setVideo(IVideo video) {
    this.video = video;
  }

  @Override
  public String toString() {
    return "AbstractCourse{" +
      "note=" + note +
      ", video=" + video +
      '}';
  }
}

这样我们就通过桥梁AbstractCourse来帮我们建立起了笔记和视频的联系

消息

image-20210105194706598

我们可以通过桥接模式来构建消息类型和发送消息的关系

image-20210106092933778
public abstract class AbastractMessage {
  private IMessage message;

  public AbastractMessage(IMessage message) {
    this.message = message;
  }

  void sendMessage(String message, String toUser) {
    this.message.send(message, toUser);
  }
}

使用如下

public class Test {
  public static void main(String[] args) {
    IMessage message = new SmsMessage();
    AbastractMessage abastractMessage = new NomalMessage(message);
    abastractMessage.sendMessage("加班申请","王总");

    message = new EmailMessage();
    abastractMessage = new UrgencyMessage(message);
    abastractMessage.sendMessage("加班申请","王总");
  }
}

感觉桥接模式和装饰者有点像

源码中的桥接模式

JDBC

image-20210105195725047

初始化

当我们执行如下代码的时候

/**
  * JDBC规定了这些步骤,但是没有具体实现。<br>
  * 这些具体实现跟抽象步骤怎么衔接起来呢?通过DriverManager来桥接<br/>
  */
try {
  //1.加载驱动  ---> 建立桥
  Class.forName("com.mysql.jdbc.Driver");  //反射机制加载驱动类,执行静态块代码,调用Driver的构造方法<br>
  // 2.获取连接Connection
  //主机:端口号/数据库名  --> 前面建立桥了 这里就用
Connection conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/ds0", "admin", "admin");
  // 3.得到执行sql语句的对象Statement
  Statement stmt = conn.createStatement();
  // 4.执行sql语句,并返回结果
  ResultSet rs = stmt.executeQuery("select *from table");
}catch (Exception e){
  e.printStackTrace();
}
}

会执行对应的静态代码块

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

java.sql.DriverManager#registerDriver(java.sql.Driver, java.sql.DriverAction)

private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();

public static synchronized 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);

}

可以看到帮我们把对应的驱动对象包装了一层变成了DriverInfo,并且存放起来了

获取连接

java.sql.DriverManager#getConnection(java.lang.String, java.util.Properties, java.lang.Class<?>)

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.getClass().getName());
  }
}

可以看到获取连接的时候,从之前已经存起来的驱动里面来获取连接,并且返回出去

通过一个集合帮我们构建了一个桥梁

总结

适用场景

  • 抽象和实现分离
    • 能够帮我们解决继承的问题
      • 单继承
      • 暴露过多属性给子类
      • 或者因为多继承导致类的个数剧增
      • 无法喜更细化场景
  • 优秀的扩展能力
    • 我们桥梁都是面向抽象的
    • 增加桥也是能够快速增加的
  • 一个类存在两个或者多个独立变化的维度
    • 这两个维度都需要独立进行扩展
  • 接口或者抽象类不稳定的场景
    • 如果通过继承,修改的会非常多
  • 重用性要求高的场景
    • 继承会导致受到父类的限制,粒度不会太细

优点

  • 分离了抽象部分和具体实现部分
    • 我们通过桥梁可以解决继承的缺点
  • 提高了系统的扩展
  • 符合开闭原则
  • 符合合成复用原则

缺点

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

和其他设计模式的关联

桥接模式更加注重形式

桥接模式更加注重连接,适配器模式更加注重适配

注意

​ 不能说继承不好,它非常好,但是有缺点,我们可以扬长避短,对 于比较明确不发生变化的,则通过继承来完成;若不能确定是否会发生 变化的,那就认为是会发生变化,则通过桥梁模式来解决,这才是一个完美的世界。

问题

想一下桥接模式怎么不使用继承的情况下怎么把两个维度结合起来的呢,可以举个例子或者对于上述案例加以说明也行

  • 在java当中除了继承就是组合和聚合了

    • 如果通过继承

      • 那么子类会持有很多父类的东西,并且很容易重写父类的方法,违背里氏替换原则
    • 如果通过组合和聚合

      • 子类想要拥有方法很简单,桥梁打过去,获得这个方法就行了
      • 例如
      image-20210106090526018
我们集团组要功能是赚钱,赚钱需要先生产然后销售`crop`当前是抽象类具体的生产产品,和销售交给旗下的子公司`HouseCrop`卖房子的和`IpodCorp`卖Ipod的来卖钱
  • public class Client {
      public static void main(String[] args) {
        System.out.println("-------房地产公司是这样运行的------");
        //先找到我的公司
        HouseCorp houseCorp = new HouseCorp();
        //看我怎么挣钱
        houseCorp.makeMoney();
        System.out.println("\n");
        System.out.println("-------服装公司是这样运行的-------");
    
        ClothesCorp clothesCorp = new ClothesCorp();
        clothesCorp.makeMoney();
    
      }
    }
    

    很明显现在有一个问题,我们的工厂和具体的产品绑的太死了,比如说但是我们不想用抽象工厂那么可以通过桥接模式来解决,用组合来把他们构建起来

    image-20210106091728955

    可以让山寨的工厂也能生产房子和ipod其实就是通过桥梁模式让我们的具体公司能够和产品关联起来

我们平常写JDBC代码的时候有没有去琢磨一下这句代码DriverManager.getConnection("jdbc:mysql://localhost:3306/ds0", "admin", "admin");是怎么拿到mysqlConnection。

image-20210106092730379

我的笔记仓库地址gitee 快来给我点个Star吧

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 前言 人在IT江湖飘,不懂设计模式咋装X? 桥接模式在日常开发中不是特别常用,主要是因为上手难度较大,但是对于理解...
    晴栀吖阅读 366评论 0 0
  • 桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供...
    Ant_way阅读 434评论 0 0
  • 桥接模式介绍 桥接模式(Bridge Pattern)也称为桥梁模式,是结构型设计模式之一。顾名思义其与现实中的桥...
    小的橘子阅读 502评论 0 0
  • 引言   咋一看这个模式,看的一头懵逼,纯概念很来理解。举个例子吧 就像一座桥,链接两端。先回顾上一节的装饰模式,...
    天道__阅读 2,054评论 0 2
  • 桥接模式 1.定义: 将抽象部分与实现部分分离,使他们都可以独立的进行变化。 2.使用场景: 一个类存在着两个独立...
    TangBuzhi阅读 405评论 0 0