桥接模式即将抽象部分与它的实现部分分离开来,使他们都可以独立变化。
桥接模式将继承关系转化成关联关系,它降低了类与类之间的耦合度,减少了系统中类的数量,也减少了代码量。
桥接模式是一种结构型设计模式 桥接模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的 功能扩展
将抽象部分与他的实现部分分离这句话不是很好理解,其实这并不是将抽象类与他的派生类分离,而是抽象类和它的派生类用来实现自己的对象。这样还是不能理解的话。我们就先来认清什么是抽象化,什么是实现化,什么是脱耦。
抽象化:其概念是将复杂物体的一个或几个特性抽出去而只注意其他特性的行动或过程。在面向对象就是将对象共同的性质抽取出去而形成类的过程。
实现化:针对抽象化给出的具体实现。它和抽象化是一个互逆的过程,实现化是对抽象化事物的进一步具体化。
脱耦:脱耦就是将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联,将两个角色之间的继承关系改为关联关系。
对于那句话:将抽象部分与他的实现部分分离套用《大话设计模式》里面的就是实现系统可能有多个角度分类,每一种角度都可能变化,那么把这种多角度分类给分离出来让他们独立变化,减少他们之间耦合。
桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合关系)而不是继承关系,从而使两者可以相对独立地变化,这就是桥接模式的用意。
模式结构桥接模式的结构与其名称一样,存在一条连接两个继承等级结构的桥
在桥接模式结构图中包含如下几个角色:
●Abstraction(抽象类):用于定义抽象类的接口,它一般是抽象类而不是接口,其中定义了一个Implementor(实现类接口)类型的对象并可以维护该对象,它与Implementor之间具有关联关系,它既可以包含抽象业务方法,也可以包含具体业务方法。
●RefinedAbstraction(扩充抽象类):扩充由Abstraction定义的接口,通常情况下它不再是抽象类而是具体类,它实现了在Abstraction中声明的抽象业务方法,在RefinedAbstraction中可以调用在Implementor中定义的业务方法。
●Implementor(实现类接口):定义实现类的接口,这个接口不一定要与Abstraction的接口完全一致,事实上这两个接口可以完全不同,一般而言,Implementor接口仅提供基本操作,而Abstraction定义的接口可能会做更多更复杂的操作。Implementor接口对这些基本操作进行了声明,而具体实现交给其子类。通过关联关系,在Abstraction中不仅拥有自己的方法,还可以调用到Implementor中定义的方法,使用关联关系来替代继承关系。
●ConcreteImplementor(具体实现类):具体实现Implementor接口,在不同的ConcreteImplementor中提供基本操作的不同实现,在程序运行时,ConcreteImplementor对象将替换其父类对象,提供给抽象类具体的业务操作方法。
在使用桥接模式时,我们首先应该识别出一个类所具有的两个独立变化的维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。通常情况下,我们将具有两个独立变化维度的类的一些普通业务方法和与之关系最密切的维度设计为“抽象类”层次结构(抽象部分),而将另一个维度设计为“实现类”层次结构(实现部分)。
现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网,打电话等)
使用桥接模式让程序具有更好的扩展性,利于程序维护
使用桥接模式对应的类图
代码
package com.dongguo.bridge;
/**
* @author Dongguo
* @date 2021/8/22 0022-13:20
* @description: 接口Implementor : 行为实现类的接口
*/
public interface Brand {
void open();
void close();
void call();
}
package com.dongguo.bridge;
/**
* @author Dongguo
* @date 2021/8/22 0022-13:20
* @description: 抽象类(Abstraction) :维护了Implementor
*/
public 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();
}
}
package com.dongguo.bridge;
/**
* @author Dongguo
* @date 2021/8/22 0022-13:21
* @description: RefinedAbstraction : 是Abstraction 抽象类的子类
*/
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(" 折叠样式手机");
}
}
package com.dongguo.bridge;
/**
* @author Dongguo
* @date 2021/8/22 0022-13:21
* @description:RefinedAbstraction : 是Abstraction 抽象类的子类
*/
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(" 直立样式手机");
}
}
package com.dongguo.bridge;
/**
* @author Dongguo
* @date 2021/8/22 0022-13:22
* @description:ConcreteImplementorA /B :行为的具体实现类
*/
public class Vivo implements Brand {
@Override
public void open() {
System.out.println(" Vivo 手机开机");
}
@Override
public void close() {
System.out.println(" Vivo 手机关机");
}
@Override
public void call() {
System.out.println(" Vivo 手机打电话");
}
}
package com.dongguo.bridge;
/**
* @author Dongguo
* @date 2021/8/22 0022-13:22
* @description: ConcreteImplementorA /B :行为的具体实现类
*/
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(" 小米手机打电话");
}
}
package com.dongguo.bridge;
/**
* @author Dongguo
* @date 2021/8/22 0022-13:23
* @description: 桥接模式的调用者
*/
public class Client {
public static void main(String[] args) {
//获取折叠式手机(样式+ 品牌)
Phone phone1 = new FoldedPhone(new XiaoMi());
phone1.open();
phone1.call();
phone1.close();
System.out.println("=======================");
Phone phone2 = new FoldedPhone(new Vivo());
phone2.open();
phone2.call();
phone2.close();
System.out.println("==============");
UpRightPhone phone3 = new UpRightPhone(new XiaoMi());
phone3.open();
phone3.call();
phone3.close();
System.out.println("==============");
UpRightPhone phone4 = new UpRightPhone(new Vivo());
phone4.open();
phone4.call();
phone4.close();
}
}
运行结果:
小米手机开机
折叠样式手机
小米手机打电话
折叠样式手机
小米手机关机
折叠样式手机
=======================
Vivo 手机开机
折叠样式手机
Vivo 手机打电话
折叠样式手机
Vivo 手机关机
折叠样式手机
==============
小米手机开机
直立样式手机
小米手机打电话
直立样式手机
小米手机关机
直立样式手机
==============
Vivo 手机开机
直立样式手机
Vivo 手机打电话
直立样式手机
Vivo 手机关机
直立样式手机
桥接模式在JDBC 的源码剖析
Jdbc 的Driver 接口,如果从桥接模式来看,Driver 就是一个接口,
下面可以有MySQL 的Driver,Oracle 的Driver,这些就可以当做实现接口类 ,那么我们现在来看看MySQL中的Driver类
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!");
}
}
}
调用了DriverManager中的registerDriver方法来注册驱动。当驱动注册完成后,我们就会开始调用DriverManager中的getConnection方法了
public class DriverManager {
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;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
// 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.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对象。在Java中通过Connection提供给各个数据库一样的操作接口,这里的Connection可以看作抽象类。可以说我们用来操作不同数据库的方法都是相同的,不过MySQL有自己的ConnectionImpl类,同样Oracle也有对应的实现类。这里Driver和Connection之间是通过DriverManager类进行桥接的。
优点 1、分离抽象接口及其实现部分。提高了比继承更好的解决方案。
2、桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
3、实现细节对客户透明,可以对用户隐藏实现细节。
缺点 1、桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
2、桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
模式使用场景 1、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
2、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
3、一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
实例:
1) -JDBC 驱动程序 2) -银行转账系统 转账分类: 网上转账,柜台转账,AMT 转账 转账用户类型:普通用户,银卡用户,金卡用户.. 3) -消息管理 消息类型:即时消息,延时消息 消息分类:手机短信,邮件消息,QQ 消息...
模式总结 1、桥接模式实现了抽象化与实现化的脱耦。他们两个互相独立,不会影响到对方。
2、对于两个独立变化的维度,使用桥接模式再适合不过了。
3、对于“具体的抽象类”所做的改变,是不会影响到客户。