定义
将抽象部分与它的具体实现部分分离,使它们都可以独立的变化。(一定程度上解耦)
通过组合的方式建立两个类之间的联系,而不是继承
适用场景
- 抽象和具体实现之间增加更多的灵活性。
- 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。
- 不希望使用继承,或因为多层继承导致系统类的个数剧增。
优点
- 分离抽象部分及其具体实现部分。(抽象和实现不在同一维度)
- 提高了系统可扩展性
- 符合开闭原则
- 合成复用原则
缺点
- 增加了系统和理解设计难度。
- 需要正确的识别出系统两个独立变化的维度。
代码
桥接模式说起来很难理解,其核心就是抽象与实现分离使用组合的方式分离开来。我们来实现这么一个业务场景,来加深理解一下桥接模式。
目前我们使用手机,都有两大主系统,安卓和IOS系统。但是这不影响我们使用微信,QQ或者其他社交软件进行聊天。
我们使用桥接模式来实现这个使用不同系统以及不同的社交软件进行聊天的业务场景。
首先需要进行抽象:
- 社交软件
- 打开软件
- 聊天功能
- 系统
- 使用系统打开软件
下面我们来进行具体的实现
社交软件代码
社交软件抽象接口SocialSofware
public interface SocialSofware {
/**
* 打开软件
* @return
*/
SocialSofware openSoftware();
/**
* 开始聊天
*/
void chat();
}
我们的抽象已经完成,接下来,让我们具体实现两个社交软件QQ
和weichat
QQ软件QQSoftware
/**
* QQ软件
*/
public class QQSoftware implements SocialSofware {
@Override
public SocialSofware openSoftware() {
System.out.println("打开QQ");
return new QQSoftware();
}
@Override
public void chat() {
System.out.println("使用QQ进行聊天");
}
}
微信软件WeichatSoftware
/**
* 微信软件
*/
public class WeichatSoftware implements SocialSofware {
@Override
public SocialSofware openSoftware() {
System.out.println("打开微信");
return new WeichatSoftware();
}
@Override
public void chat() {
System.out.println("使用微信进行聊天");
}
}
操作系统代码
抽象类操作系统PhoneOs
这里为什么使用抽象类呢,因为我们将聊天软件引入到操作系统中,需要使用组合模式,让子类来具体实现。
/**
* 手机操作系统
*/
public abstract class PhoneOs {
/**
* 只有子类可以调用
*/
protected SocialSofware socialSofware;
public PhoneOs(SocialSofware socialSofware) {
this.socialSofware = socialSofware;
}
/**
* 打开软件
* @return
*/
abstract SocialSofware open();
}
苹果操作系统IosPhone
/**
* 苹果操作系统
*/
public class IosPhone extends PhoneOs{
public IosPhone(SocialSofware socialSofware) {
super(socialSofware);
}
@Override
SocialSofware open() {
System.out.println("苹果系统打开软件");
return socialSofware.openSoftware();
}
}
安卓操作系统AndroidPhone
/**
* 安卓手机
*/
public class AndroidPhone extends PhoneOs {
public AndroidPhone(SocialSofware socialSofware) {
super(socialSofware);
}
@Override
SocialSofware open() {
System.out.println("安卓系统打开软件");
return socialSofware.openSoftware();
}
}
我们可以看到这两个操作系统具体实现类,通过组合将社交软件引入进来,并且调用其打开方法进行打开,这样无论社交软件打开方式怎么变,具体实现类不受影响。
UML类图
代码测试
BridgeTest
public class BridgeTest {
public static void main(String[] args) {
/**
* IOS打开QQ
*/
PhoneOs iosQQ = new IosPhone(new QQSoftware());
QQSoftware qqSoftware = (QQSoftware) iosQQ.open();
qqSoftware.chat();
/**
* 安卓打开weichat
*/
PhoneOs androidWeiChat = new AndroidPhone(new WeichatSoftware());
WeichatSoftware weichatSoftware = (WeichatSoftware) androidWeiChat.open();
weichatSoftware.chat();
}
}
我们来使用不同系统打开不同社交软件看一下结果如何:
我们可以看到 我们创建了某个系统并且调用了open
方法来委托社交软件来执行openSofeware
方法。所以我们这个 桥 就搭建完成了。
其他源码使用
我们来看一下java.sql.DriverManager
类中的 registerDriver(java.sql.Driver, java.sql.DriverAction)
方法
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);
}
这个方法相当于将每个数据库驱动抽象。然后通过getConnection
方法来进获取对应的连接。
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");
}
小结
桥接模式与 适配器模式或者组合模式都比较相像,希望我们可以区分开来,并且适当的运用。