桥梁模式

桥接模式的一个常用使用场景就是为了替换继承,继承拥有很多优点,比如抽象,封装,多态等,父类封装共性,子类实现特性。继承可以很好地帮助我们实现代码复用或封装的功能,但是同时,这也是继承的一大缺点。因为父类拥有的方法,子类也会继承得到,无论子类需不需要,这说明了继承具备强侵入性父类代码侵入子类,同时会导致子类臃肿,在设计模式中,有一个原则:优先使用组合或聚合的方式,而不是继承。

定义

桥接模式Bridge Pattern也称为桥梁模式接口模式柄体模式,是将抽象部分与它的具体实现部分分离,即解耦抽象和实现,使它们都可以独立地变化,属于结构型模式

桥接模式主要目的是通过组合的方式建立两个类之间的联系,而不是继承。但又类似于多重继承方案,但是多重继承方案往往违背了类的单一职责原则,其复用性比较差,桥接模式是比多重继承更好的替代方案,桥接模式的核心在于解耦抽象和实现

此处的抽象并不是指抽象类或接口这种高层概念实现也不是继承或接口实现抽象实现其实指的是两种独立变化的维度。抽象包含实现,因此一个抽象类的变化可能涉及到多种维度的变化导致的。

实现

桥接模式主要包含四种角色:

  • 抽象化角色(Abstraction):该类持有一个对实现角色的引用抽象角色中的方法需要实现角色来实现,抽象角色一般为抽象类
  • 修正抽象化角色(RefinedAbstraction):Abstraction的具体实现,对Abstraction的方法进行完善扩展
  • 实现化角色(Implementor):确定实现维度的基本操作,提供给Abstraction使用,该类一般为接口或抽象类
  • 具体实现化角色(Concretelmplementor):Impementor的具体实现
抽象化角色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class Corp {
// 定义一个抽象的产品对象,不知道具体是什么产品
private Product product;

// 构造函数,由子类定义传递具体的产品进来
public Corp(Product product) {
this.product = product;
}

//公司是干什么的? 赚钱的!
public void makeMoney() {
//每家公司都是一样,先生产
this.product.beProducted();
//然后销售
this.product.beSelled();
}
}
修正抽象化角色
1
2
3
4
5
6
7
8
9
10
11
public class ShanZhaiCorp extends Corp {
// 产什么产品,不知道,等被调用的才知道
public ShanZhaiCorp(Product product) {
super(product);
}
// 狂赚钱
public void makeMoney() {
super.makeMoney();
System.out.println("我赚钱呀...");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class HouseCorp extends Corp {
//定义传递一个House产品进来
public HouseCorp(House house) {
super(house);
}

//房地产公司很High了,赚钱,计算利润
public void makeMoney() {
super.makeMoney();
System.out.println("房地产公司赚大钱了...");
}
}
实现化角色
1
2
3
4
5
6
public abstract class Product {
// 甭管是什么产品它总要能被生产出来
public abstract void beProducted();
// 生产出来的东西,一定要销售出去
public abstract void beSelled();
}
具体实现化角色
1
2
3
4
5
6
7
8
9
10
public class House extends Product {
// 豆腐渣就豆腐渣呗,好歹也是房子
public void beProducted() {
System.out.println("生产出的房子是这样的...");
}
// 虽然是豆腐渣,也是能够销售出去的
public void beSelled() {
System.out.println("生产出的房子卖出去了...");
}
}
1
2
3
4
5
6
7
8
9
public class IPod extends Product {
public void beProducted() {
System.out.println("生产出的iPod是这样的...");
}

public void beSelled() {
System.out.println("生产出的iPod卖出去了...");
}
}
Client使用
1
2
3
4
5
6
7
8
House house = new House();
System.out.println("-------房地产公司是这样运行的-------");
HouseCorp houseCorp =new HouseCorp(house);
houseCorp.makeMoney();
System.out.println("\n");
System.out.println("-------山寨公司是这样运行的-------");
ShanZhaiCorp shanZhaiCorp = new ShanZhaiCorp(new Clothes());
shanZhaiCorp.makeMoney();

优点

  • 分离抽象部分及其具体实现部分
  • 提高了系统的扩展性
  • 符合开闭原则
  • 符合合成复用原则

缺点

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

应用

当一个类内部具备两种或多种变化维度时,使用桥接模式可以解耦这些变化的维度,使高层代码架构稳定。桥接模式适用于以下几种业务场景:

  • 在抽象和具体实现之间需要增加更多的灵活性的场景
  • 一个类存在两个或多个独立变化的维度,而这两个或多个维度都需要独立进行扩展
  • 不希望使用继承,或因为多层继承导致系统类的个数剧增

扩展

JDBC中的Driver类就是桥接对象,Driver在JDBC中并没有做任何实现,具体的功能实现由各厂商完成,

1
2
3
4
5
6
7
8
//加载驱动
Class.forName("com.mysql.jdbc.Driver");//反射机制加载驱动类
// 获取连接Connection,主机:端口号/微据库名
connection conn= DriverManager.getconnection("jdbc:mysql://localhost:33e6/test","root","root");
// 得到执行sql语句的对象Statement
statement stmt = conn.createstatement();
// 执行sql语句,并返回结果
Resultset rs = stmt.executeQuery("select *from table");

当执行Class.forName("com.myqljdbc.Driver")方法的时候,就会执行com.mysql.jdbc.Driver这个类的静态块中的代码,DriverManager的registerDriver()方法将Driver对象注册到DriverManager中,DriverManager就是桥,连接各个数据库厂商的具体实现。

1
2
3
4
5
6
7
8
9
10
11
public class Driver extends NonRegisteringpriver implements java.sql.Driver {
public Driver() throws sQLexception {
}
static {
try{
DriverManager.registeroriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}