桥接模式( Bridge Pattern )也称为桥梁模式、接口(Interfce)模式或柄体( Handle and Body)模 式,是将抽象部分与它的具体实现部分分离,使它们都可以独立地变化,属于结构型模式。
原文:Decouple an abstraction from its implementation so that the two can vary independently.
翻译:解锅抽象和实现,使得两者可以独立的变化。
也可以理解成:一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。
桥接模式主要目的是通过组合的方式建立两个类之间的联系,而不是继承。但又类似于多重继承方 案,但是多重继承方案往往违背了类得单一职责原则,其复用性比较差,桥接模式是比多重继承更好的 替代方案。桥接模式的核心在于解耦抽象和实现。
注:此处的抽象并不是指抽象类象接口这种商层概念,实现也不是继承或接口实现。抽象与实现其实指的是两种独立 变化的维度。
具体代码:
- public abstract class Abstraction {
- private final Implementor implementor;
- public Abstraction(Implementor implementor){
- this.implementor = implementor;
- }
-
- public void function(){
- implementor.implementation();
- }
- }
- 复制代码
- public class RefinedAbstraction extends Abstraction{
- public RefinedAbstraction(Implementor implementor) {
- super(implementor);
- }
-
- public void refinedFunction(){
- //子类扩展方法
- super.implementor.implementation();
- }
-
- }
- 复制代码
- public interface Implementor {
- void implementation();
- }
- 复制代码
- public class ConcreteImplementorA implements Implementor{
- @Override
- public void implementation() {
- System.out.println("ConcreteImplementorA");
- }
- }
- 复制代码
- public class ConcreteImplementorB implements Implementor{
- @Override
- public void implementation() {
- System.out.println("ConcreteImplementorB");
- }
- }
- 复制代码
- public class Test {
- public static void main(String[] args) {
- Implementor implementor = new ConcreteImplementorA();
- RefinedAbstraction refinedAbstraction = new RefinedAbstraction(implementor);
- refinedAbstraction.refinedFunction();
- }
- }
- 复制代码
举个例子,家用电器,有各种家用电器比如洗衣机、冰箱、空调,所有的家用电器又对应这自己的品牌,这就分离出来了两个维度一个是家用电器,还一个是品牌两个都能扩展,这里就很适合使用桥接模式 具体代码如下: 首先把家用电器抽象出来
- public abstract class Product {
- protected ICompany company;
-
-
- public Product(ICompany company) {
- this.company = company;
- }
-
- public abstract void printProductInfo();
- }
- 复制代码
具体实现
- public class AirConditioner extends Product{
- public AirConditioner(ICompany company) {
- super(company);
- }
- @Override
- public void printProductInfo() {
- System.out.print(company.getName());
- System.out.print("公司---");
- System.out.println("空调");
- }
- }
- 复制代码
- public class Washer extends Product{
- public Washer(ICompany company) {
- super(company);
- }
- @Override
- public void printProductInfo() {
- System.out.print(company.getName());
- System.out.print("公司---");
- System.out.println("洗衣机");
- }
- }
- 复制代码
然后就是公司的接口
- public interface ICompany {
- String getName();
- }
- 复制代码
具体实现:
- public class Haier implements ICompany{
- @Override
- public String getName() {
- return "海尔";
- }
- }
- 复制代码
- public class Meidi implements ICompany{
- @Override
- public String getName() {
- return "美的";
- }
- }
- 复制代码
最后使用方式:
- public class Test {
- public static void main(String[] args) {
- Product product = new Washer(new Haier());
- Product product1 = new Washer(new Meidi());
- product.printProductInfo();
- product1.printProductInfo();
- }
- }
- 复制代码
大家非常熟悉的 JDBC API,其中有一个Driver
类就是桥接对象。我们都知道,我们在使用的时候 通过Class.forName()
方法可以动态加载各个数据库厂商实现的 Driver
类。具体客户端应用代码如下, 以 MySQL的实现为例
- Class.forName("com.mysql.jdbc.Driver");
- Connection conn= DriverManager.getConnection("jdbc:mysql://1ocalhost:3306/test","root","root");
- Statement stmt = conn.createStatement();
- ResultSet resultSet = stmt.executeQuery("select *from table");
- 复制代码
先看Driver
这个类
- public interface Driver {
- Connection connect(String url, java.util.Properties info)
- throws SQLException;
- boolean acceptsURL(String url) throws SQLException;
- DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info)
- throws SQLException;
- int getMinorVersion();
- boolean jdbcCompliant();
- public Logger getParentLogger() throws SQLFeatureNotSupportedException;
- }
- 复制代码
发现SDK提供的Driver
类是一个接口
mysql实现了这个接口 可以看到当加载Driver
这个类的时候会执行
- DriverManager.registerDriver(new Driver());
- 复制代码
- public static void registerDriver(java.sql.Driver driver)
- throws SQLException {
-
- registerDriver(driver, null);
- }
- 复制代码
- public static 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);
-
- }
- 复制代码
JDK底层是把mysql 的Driver
的实例包装成了 DriverInfo
对象.
接下来再看 getConnection
方法
- @CallerSensitive
- 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()));
- }
- 复制代码
- private static Connection getConnection(
- String url, java.util.Properties info, Class> caller) throws SQLException {
- /*
- * When callerCl is null, we should check the application's
- * (which is invoking this class indirectly)
- * classloader, so that the JDBC driver class outside rt.jar
- * can be loaded from here.
- */
- ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
- if (callerCL == null || callerCL == ClassLoader.getPlatformClassLoader()) {
- callerCL = Thread.currentThread().getContextClassLoader();
- }
-
- if (url == null) {
- throw new SQLException("The url cannot be null", "08001");
- }
-
- println("DriverManager.getConnection("" + url + "")");
-
- ensureDriversInitialized();
-
- // Walk through the loaded registeredDrivers attempting to make a connection.
- // Remember the first exception that gets raised so we can reraise it.
- SQLException reason = null;
-
- 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.driver.getClass().getName());
- }
-
- }
-
- // if we got here nobody could connect.
- if (reason != null) {
- println("getConnection failed: " + reason);
- throw reason;
- }
-
- println("getConnection: no suitable driver found for "+ url);
- throw new SQLException("No suitable driver found for "+ url, "08001");
- }
-
-
- }
- 复制代码
可以看到这个 Connection
就是调用的额mysql实现的 driver
类返回的。
所以真实操作数据库的代码都是各个厂商根据自己数据库生成的,jdk只是统一了接口,所以切换数据库只需要切换驱动就行了。
这其中 DriverManager
就相当于 Abstraction
角色 Driver
就是 Implementor
角色,每个厂商对Driver
的实现就是ConcreteImplementor
角色
桥接(Bridge)模式的定义是:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
通过上面的讲解,我们能很好的感觉到桥接模式遵循了里氏替换原则和依赖倒置原则,最终实现了开闭原则,对修改关闭,对扩展开放。这里将桥接模式的优缺点总结如下:
优点:
缺点: