设计模式引入
手机操作问题:
现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网,打电话等),如图:
如果按照我们初学Java编程可以很容易实现,思路呢类似于以下图:
从功能上来说,这样设计是可以的。但是,若是增加一种新的样式,那么同样的它也会新增不同品牌手机的子类,这样就会导致类爆炸的问题。
如果添加一个旋转系列的手机,就会做如下操作
传统方案解决手机操作问题分析:
-
扩展性问题(类爆炸),如果我们再增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加
-
违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这样增加了代码维护成本。
-
可以通过桥接模式解决这个问题。
桥接模式
基本介绍
-
桥接模式(Bridge模式)是指:将实现和抽象放在两个不同的层次类中,使两个层次的类可以独立改变;
-
桥接模式是一种结构设计模式;
-
Bridge模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展;
桥接模式原理图
原理类图说明
-
Client类:桥接模式的调用者;
-
抽象类(Abstraction) :维护了Implementor /即它的实现类ConcreteImplementorA..,二者是聚合关系, Abstraction充当桥接类;
-
RedefinedAbstraction:是Abstraction抽象类的子类;
-
Implementor:行为实现类的接口;
-
ConcreteImplementorA/B:行为的具体实现类。
从UML类图中可以看出,这里的抽象类和接口是聚合的关系,也就是调用和被调用的关系。
解决引入的手机问题
类图
源码
-
品牌 接口
// 接口 public interface Brand { void open(); void close(); void call(); }
// 接口 public interface Brand { void open(); void close(); void call(); }public abstract class Phone { //组合品牌 private Brand brand; //构造器 public Phone(Brand brand) { super(); this.brand = brand; } protected void open() { this.brand.open(); } protected void close() { brand.close(); } protected void call() { brand.call(); } }
public abstract class Phone { //组合品牌 private Brand brand; //构造器 public Phone(Brand brand) { super(); this.brand = brand; } protected void open() { this.brand.open(); } protected void close() { brand.close(); } protected void call() { brand.call(); } }public class XiaoMi implements Brand { @Override public void open() { System.out.println(" 小米手机开机 "); } @Override public void close() { System.out.println(" 小米手机关机 "); } @Override public void call() { System.out.println(" 小米手机打电话 "); } }
public class XiaoMi implements Brand { @Override public void open() { System.out.println(" 小米手机开机 "); } @Override public void close() { System.out.println(" 小米手机关机 "); } @Override public void call() { System.out.println(" 小米手机打电话 "); } }public class HuaWei implements Brand { @Override public void open() { System.out.println(" 华为手机开机 "); } @Override public void close() { System.out.println(" 华为手机关机 "); } @Override public void call() { System.out.println(" 华为手机打电话 "); } }
public class HuaWei implements Brand { @Override public void open() { System.out.println(" 华为手机开机 "); } @Override public void close() { System.out.println(" 华为手机关机 "); } @Override public void call() { System.out.println(" 华为手机打电话 "); } }public class UpRightPhone extends Phone { //构造器 public UpRightPhone(Brand brand) { super(brand); } public void open() { super.open(); System.out.println(" 直立样式手机 "); } public void close() { super.close(); System.out.println(" 直立样式手机 "); } public void call() { super.call(); System.out.println(" 直立样式手机 "); } }
public class UpRightPhone extends Phone { //构造器 public UpRightPhone(Brand brand) { super(brand); } public void open() { super.open(); System.out.println(" 直立样式手机 "); } public void close() { super.close(); System.out.println(" 直立样式手机 "); } public void call() { super.call(); System.out.println(" 直立样式手机 "); } }//折叠式手机类,继承 抽象类 Phone public class FoldedPhone extends Phone { public FoldedPhone(Brand brand) { super(brand); } public void open() { super.open(); System.out.println(" 折叠样式手机 "); } public void close() { super.close(); System.out.println(" 折叠样式手机 "); } public void call() { super.call(); System.out.println(" 折叠样式手机 "); } }
//折叠式手机类,继承 抽象类 Phone public class FoldedPhone extends Phone { public FoldedPhone(Brand brand) { super(brand); } public void open() { super.open(); System.out.println(" 折叠样式手机 "); } public void close() { super.close(); System.out.println(" 折叠样式手机 "); } public void call() { super.call(); System.out.println(" 折叠样式手机 "); } }public class Client { public static void main(String[] args) { //获取折叠式手机 (样式 + 品牌 ) Phone phoneXiaomi = new FoldedPhone(new XiaoMi()); phoneXiaomi.open(); phoneXiaomi.call(); phoneXiaomi.close(); System.out.println("======================="); Phone phoneHuaWei= new FoldedPhone(new HuaWei()); phoneHuaWei.open(); phoneHuaWei.call(); phoneHuaWei.close(); System.out.println("======================="); UpRightPhone phone3 = new UpRightPhone(new XiaoMi()); phone3.open(); phone3.call(); phone3.close(); } }
public class Client { public static void main(String[] args) { //获取折叠式手机 (样式 + 品牌 ) Phone phoneXiaomi = new FoldedPhone(new XiaoMi()); phoneXiaomi.open(); phoneXiaomi.call(); phoneXiaomi.close(); System.out.println("======================="); Phone phoneHuaWei= new FoldedPhone(new HuaWei()); phoneHuaWei.open(); phoneHuaWei.call(); phoneHuaWei.close(); System.out.println("======================="); UpRightPhone phone3 = new UpRightPhone(new XiaoMi()); phone3.open(); phone3.call(); phone3.close(); } }Class.forName("com.mysql.jdbc.Driver"); Connection conn= DriverManager.getConnection(url,user,password);
Class.forName("com.mysql.jdbc.Driver"); Connection conn= DriverManager.getConnection(url,user,password);一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。可以考虑桥接模式。在设计系统的时候,尤其是对外开放的系统,一定要考虑这种变化可能带来的影响。以前没注意过这个jdbc的实现,粗浅得了解了下,真佩服啊… 设计模式的学习不要拘泥于局部代码,要从整体去把握。
思考
-
JDBC 驱动程序
-
银行转账系统:
-
转账分类:网上转账、柜台转账、ATM转账
-
转账用户类型:普通用户、金卡用户、钻石用户
-
-
消息关系
-
消息类型:即时消息、延时消息
-
消息分类:手机短信、邮件消息、QQ消息
-
应用场景
-
实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统;
-
对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成;
-
桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
-
桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程;
-
桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性,即需要有这样的应用场景。
桥接模式的注意事项和细节
对于应用程序而言,只要选用不同的驱动,就可以让程序操作不同的数据库,而无需更改应用程序,从而实现在不同的数据库上移植; 对于驱动程序而言,为数据库实现不同的驱动程序,并不会影响应用程序
-
数据库驱动
-
应用程序的数据库连接信息(可能是mysql,可能是oracel等)
我们分析下,这里和桥接模式有什么关系。 回顾一下桥接模式的定义:将抽象部分与实现部分分离,使它们可以独立地进行定义。 为两个独立变化的维度设计两个独立的继承等级结构,并在抽象层建立一个抽象关联。 这里两个独立变化的维度:
public class DriverManager { public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException { registerDriver(driver, null); } 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); } }
Class.forName会把类加载进来,执行类的静态方法(类加载的初始化阶段)。我们看到mysql Driver的static块,调用了java.sql.DriverManager的registerDriver方法
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!"); } } }
com.mysql.jdbc.Driver 源码
加载com.mysql.jdbc.Driver类
源码剖析
我们知道jdbc只提出了一系列接口规范,具体的实现由数据库提供者去实现。所以,当我们在连接mysql的时候,需要添加jdbc-mysql.jar,在连接oracel的时候,需要添加ojdbc.jar(不确定jar名有没有写错,没在代码里用过oracel)。然后去载入驱动,再进行连接
JDBC源码中使用的桥接模式
由上面的类图我们可以看出来,当我们增加苹果手机品牌和旋转样式的时候,只需要增加两个类。并不需要像传统模式那样添加N多类和方法了,节省了大量的开发时间,也变得合理了很多很多。
看完上述代码,我们可以体会到桥接模式的妙处了~
-
客户端
-
折叠式手机 实现
-
直立式手机 实现
-
具体手机品牌 - 华为
-
具体手机品牌 - 小米
-
手机样式 抽象类
-