桥接设计模式( 99%的面试官都不懂)(一)
定义
Bridge Design Pattern,将抽象部分与它的实现部分分离,使之任意删减,而无需受其它约束。
Decouple an abstraction from its implementation so that the two can vary independently,将抽象和实现解耦,让它们可以独立变化。
结构型模式。
结构
• Abstraction:定义抽象类的接口,维护一个指向Implementor类型对象的指针,将Client的请求转发给它的Implementor.RefinedAbstraction扩充由Abstraction定义的接口.定义了基于基本操作的较高层次的操作
• RefinedAbstraction:扩充由Abstraction定义的接口而得的 抽象类
• Implementor:定义实现类的接口.仅提供基本操作
• ConcreteImplementor:实现Implementor接口并定义它的具体实现
分析
理解桥接模式,重点需要理解如何将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化。
抽象化:抽象化就是忽略一些信息,把不同的实体当作同样的实体对待。在面向对象中,将对象的共同性质抽取出来形成类的过程即为抽象化的过程。
实现化:针对抽象化给出的具体实现,就是实现化,抽象化与实现化是一对互逆的概念,实现化产生的对象比抽象化更具体,是对抽象化事物的进一步具体化的产物。
脱耦:脱耦就是将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联,将两个角色之间的继承关系改为关联关系。桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合)而不是继承,从而使两者可以相对独立地变化,这就是桥接模式的用意。
也可理解成:“一个类存在两个(或多个)独立变化的维度,通过组合,让这两个(或多个)维度可独立进行扩展。”组合替代继承,就能避免继承层次的指数级爆炸,即“组合优于继承”设计原则。
优点
• 使接口与实现各自独立
• 师接口实现类的扩展性大大增强
• 保护了部分实现内容,在扩展与变更内容时,无须重新编译原客户程序
• 桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法。
• 实现细节对客户透明,可以对用户隐藏实现细节。
缺点
• 增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程
• 要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
适用场景
基于此,以下情形可考虑此模式进行设计与实施
• 需要增强抽象与具体角色之间的灵活性,以避免两者之间的直接关联
• 实现类的变动,不影响客户端的使用
• 抽象接口与类的实现通过组合,均可在子类上进行扩展
案例
JDBC驱动
经典应用,利用JDBC驱动查询数据库:
Class.forName("com.mysql.jdbc.Driver");//加载及注册JDBC驱动程序
String url = "jdbc:mysql://localhost:3306/sample_db?user=root&password=your_password";
Connection con = DriverManager.getConnection(url);
Statement stmt = con.createStatement();
String query = "select * from test";
ResultSet rs=stmt.executeQuery(query);
while(rs.next()) {
rs.getString(1);
rs.getInt(2);
}
若想把MySQL换成Oracle,只需将第一行代码中的com.mysql.jdbc.Driver换成oracle.jdbc.driver.OracleDriver。还有更灵活实现方式,把需要加载的Driver类写到配置文件,程序启动时自动从配置文件加载,切换数据库时,就无需修改代码,仅修改配置文件然后发布即可。
不管改代码or配置,从一个DB切到另一种DB,都只需改动少代码或完全不需改代码,如此轻量级的数据库切换操作是如何实现的?
package com.mysql.jdbc;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
/**
* Construct a new driver and register it with DriverManager
* @throws SQLException if a database error occurs.
*/
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
}
执行**Class.forName(“com.mysql.jdbc.Driver”)**时,其实执行了:
• 要求JVM查找并加载指定Driver类
• 执行该类的静态代码,即:将MySQL Driver注册到DriverManager类
DriverManager干嘛的?把具体Driver实现类(如com.mysql.jdbc.Driver)注册到DriverManager后,后续所有对JDBC接口的调用,都会委派到具体Driver实现类执行。而Driver实现类都实现了相同接口(java.sql.Driver ),这也是灵活切换Driver原因。
public class DriverManager {
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();
//...
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
//...
public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException {
if (driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver));
} else {
throw new NullPointerException();
}
}
public static Connection getConnection(String url, String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}
//...
}
桥接模式的定义“将抽象和实现解耦,让它们可以独立变化”。JDBC案例里,啥是“抽象”?啥是“实现”?:
• JDBC本身就相当于“抽象”。这里的“抽象”并非“抽象类”或“接口”,而是和具体的数据库无关的、被抽象出来的一套“类库”
• 具体Driver(如com.mysql.jdbc.Driver)就是“实现”。这里的“实现”,并非指“接口实现类”,而是和具体DB相关的一套“类库”
JDBC、Driver分别独立开发,通过对象之间的组合关系协作。JDBC所有逻辑操作,最终都委托给具体Driver执行。
文章转自公众号: JavaEdge