一、基本介绍
桥接模式(Bridge Pattern)是一种结构型设计模式,基于类的最小设计原则,通过使用封装、聚合及继承等方式让不同的类承担不同的职责,从而把抽象(Abstraction)和实现(Implementation)分离开放在不同的层次中,使得各部分保持独立更加利于扩展。一句话来说,就是在抽象与实现之间提供一个桥梁,降低二者耦合度,达到二者可以独立变化而不互相影响的目的。
二、从“类爆炸”说起
🌰 假如,现在有这样一个业务场景,需要对不同颜色不同品牌的手机实现相应的功能,如打电话、发短信、上网等。
如果使用传统的方式,可能是这样的,类图如下:
从以上的类图中,可以看出存在的问题,首先,这种方式会带来扩展性的问题,随着功能和手机品牌的增加,需要维护的类的数量也会增加,扩展性极差😖,产生“类爆炸”;其次,从类的最小设计原则考虑,当需要手机的颜色时,同时还需增加所有品牌的手机,这种设计违反了单一职责原则。
三、桥接模式
那么,基于以上两点的考虑,借鉴单一职责原则的核心思想,尝试将对象解耦,将类的功能职责粒度分解细化,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。
3.1 采用桥接模式解决问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
public interface PhoneBrand {
void call();
void sendMessage();
void goOnline(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public class HuaWeiPhoneBrandImpl implements PhoneBrand { @Override public void call() { System.out.println("Call use HuaWei phone."); }
@Override public void sendMessage() { System.out.println("SendMessage use HuaWei phone."); }
@Override public void goOnline() { System.out.println("GoOnline use HuaWei phone."); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public class ApplePhoneBrandImpl implements PhoneBrand { @Override public void call() { System.out.println("Call use Apple phone."); }
@Override public void sendMessage() { System.out.println("SendMessage use Apple phone."); }
@Override public void goOnline() { System.out.println("GoOnline use Apple phone."); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public class XiaoMiPhoneBrandImpl implements PhoneBrand { @Override public void call() { System.out.println("Call use XiaoMi phone."); }
@Override public void sendMessage() { System.out.println("SendMessage use XiaoMi phone."); }
@Override public void goOnline() { System.out.println("GoOnline use XiaoMi phone."); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
public abstract class Phone {
private PhoneBrand phoneBrand;
public Phone(PhoneBrand phoneBrand) { super(); this.phoneBrand = phoneBrand; }
protected void call() { this.phoneBrand.call(); }
protected void sendMessage() { this.phoneBrand.sendMessage(); }
protected void goOnline() { this.phoneBrand.goOnline(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
public class PhoneBlack extends Phone{
public PhoneBlack(PhoneBrand phoneBrand) { super(phoneBrand); }
@Override public void call() { super.call(); System.out.println("The phone of call is Black."); }
@Override public void sendMessage() { super.sendMessage(); System.out.println("The phone of sendMessage is Black."); }
@Override public void goOnline() { super.goOnline(); System.out.println("The phone of goOnline is Black."); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
public class PhoneRed extends Phone{
public PhoneRed(PhoneBrand phoneBrand) { super(phoneBrand); }
@Override public void call() { super.call(); System.out.println("The phone of call is Red."); }
@Override public void sendMessage() { super.sendMessage(); System.out.println("The phone of sendMessage is Red."); }
@Override public void goOnline() { super.goOnline(); System.out.println("The phone of goOnline is Red."); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
public class PhoneWhite extends Phone{
public PhoneWhite(PhoneBrand phoneBrand) { super(phoneBrand); }
@Override public void call() { super.call(); System.out.println("The phone of call is White."); }
@Override public void sendMessage() { super.sendMessage(); System.out.println("The phone of sendMessage is White."); }
@Override public void goOnline() { super.goOnline(); System.out.println("The phone of goOnline is White."); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
public class Client {
public static void bridgeTest() { PhoneRed phoneRed = new PhoneRed(new HuaWeiPhoneBrandImpl()); phoneRed.call(); phoneRed.sendMessage(); phoneRed.goOnline(); PhoneBlack phoneBlack = new PhoneBlack(new HuaWeiPhoneBrandImpl()); phoneBlack.call(); phoneBlack.sendMessage(); phoneBlack.goOnline(); PhoneWhite phoneWhite = new PhoneWhite(new XiaoMiPhoneBrandImpl()); phoneWhite.call(); phoneWhite.sendMessage(); phoneWhite.goOnline(); } }
|
至此,解决问题的方案改进完毕,通过这种方式,如果要添加新品牌的手机,只需让其实现PhoneBrand类即可;如果需要增加手机的颜色,只需让其继承Phone类即可。看下类图,如下:
在这个方案中,Phone这个抽象类是所有具体手机的父类,也是PhoneBrand类聚合的对象,起到了关键的“桥”的作用。基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责,将抽象与实现分离开保证不同层次独立改变互不影响更,从而极大的提高了系统的灵活性,有利于扩展。
四、桥接模式在JDBC中的应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
public class Client {
public static void jdbcTest(){ try { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("url","user","password"); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("s"); while(rs.next()){ System.out.println(rs); } rs.close(); stmt.close(); conn.close(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException throwables) { throwables.printStackTrace(); } } }
|
- 然后,看下相关类的UML类图
从UML类图可以看出,com.mysql.jdbc.Driver类是java.sql.Driver接口的实现类,注册驱动时,会执行com.mysql.jdbc.Driver类的静态块,该静态代码块中调用java.sql.DriverManager#registerDriver(java.sql.Driver)方法,将Driver对象注册到 DriverManager中,DriverManager就相当于是“桥”;调用java.sql.DriverManager#getConnection(java.lang.String, java.lang.String, java.lang.String)创建一个连接对象时,会根据驱动的类型调用不同类型数据库(mysql、oracle等)实现的 Driver 的 connect() 方法,最终获得连接对象。
附:本次演示的项目地址
https://github.com/syshlang/java-design-patterns