一、桥接模式介绍
1. 解决的问题
主要解决在有多种可能变化的情况下,用继承会造成类爆炸的问题,扩展起来不灵活。
2. 定义
桥接模式是一种结构型设计模式,可以将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次架构,从而在开发时分别使用。
简单来说,就是通过讲抽象部分和实现部分分离,把多种可匹配的使用进行组合。核心实现是在 A 类包含 B 类接口,通过构造函数实现 B 类的传递,这个 B 类就是设计的桥。
3. 应用场景
- 拆分或重组一个具有多重功能的庞杂类(比如能与多个数据库服务器进行交互的类)。
- 希望在几个独立维度上扩展一个类。
即每个维度抽取独立的类层次,初始类将相关工作委派给所属对应类层次的对象,无需自己完成所有工作。
- 在运行时需要切换不同的实现方法。
这种程度上与策略模式很相似,因为都基于组合模式——即将工作委派给其他对象。
但实际上,桥接模式不仅定义了实现部分的接口,也定义了抽象部分的接口,抽象部分和实现部分都是可可扩展的,两者可以不断组合成各种形式的结构,这也体现了结构模式的特点:通过继承、聚合的方式组合类和对象已形成更大的结构。
而策略模式关注点在于策略接口的各种实现,只是为调用策略模式的上下文提供了一种协作,不涉及两者结构的互为组合,所以它是行为模式的一种。行为模式的主要特点就是处理对象之间的通信方式,往往是通过中介者对象将通信双方解耦,而策略模式就是将上下文和实际的算法提供解耦。
因此桥接模式与策略模式相比,结构更为复杂,表达的内容更多。桥接模式主要表达的是将本质是两个并不内聚的体系区别开来,使得它们可以松散的组合。可以认为桥接中必然存在着策略模式。
二、桥接模式优缺点
1. 优点
- 可以创建与平台无关的类和程序。
- 客户端代码仅与高层抽象部分进行交互,不会接触到平台的详细信息。
- 开闭原则:可以新增抽象部分和实现部分,且它们之间不会互相影响。
- 单一职责原则:抽象部分专注于处理高层逻辑,实现部分处理平台细节。
2. 缺点
- 对于高内聚的类,使用该模式可能让代码更加复杂。
三、桥接模式应用实例:第三方登录
1. 实例场景
上线一个新产品,为了减少本地注册的繁琐而带来的隐形用户流失,提高注册转化率,通常需要支持第三方登录,这样也可以通过授权获取用户在第三方平台的社交信息,可以后续针对用户的社交关系网分析用户属性,从而进行有目的性的营销宣传。
接下来就以支持第三方登录作为模拟场景。
2. 桥接模式实现
2.1 工程结构
bridge-pattern
└─ src
├─ main
│ └─ java
│ └─ org.design.pattern.bridge
│ ├─ model
│ │ ├─ LoginApi.java
│ │ ├─ WxApi.java
│ │ └─ QqApi.java
│ └─ service
│ ├─ LoginService.java
│ └─ impl
│ └─ LoginServiceImpl.java
└─ test
└─ java
└─ org.design.pattern.bridge.test
└─ LoginServiceTest.java
2.2 代码实现
2.2.1 第三方Api
登录接口
/**
* 登录Api
*/
public interface LoginApi {
boolean login();
}
微信Api
/**
* 微信Api
*/
public class WxApi implements LoginApi {
private final Logger log = LoggerFactory.getLogger(LoginApi.class);
private String $loginApi;
private String $apiKey;
public WxApi(String $loginApi, String $apiKey) {
this.$loginApi = $loginApi;
this.$apiKey = $apiKey;
}
@Override
public boolean login() {
log.info("微信登录成功");
return true;
}
}
QQ Api
/**
* QQApi
*/
public class QqApi implements LoginApi {
private final Logger log = LoggerFactory.getLogger(LoginApi.class);
private String $loginApi;
private String $apiKey;
public QqApi(String $loginApi, String $apiKey) {
this.$loginApi = $loginApi;
this.$apiKey = $apiKey;
}
@Override
public boolean login() {
log.info("QQ登录成功");
return true;
}
}
2.2.2 登录服务
登录服务
/**
* 登录服务
*/
public abstract class LoginService {
protected final Logger log = LoggerFactory.getLogger(LoginService.class);
protected LoginApi loginApi;
public LoginService(LoginApi loginApi) {
this.loginApi = loginApi;
}
public abstract boolean login();
}
登录服务实现
/**
* 登录服务实现类
*/
public class LoginServiceImpl extends LoginService {
public LoginServiceImpl(LoginApi loginApi) {
super(loginApi);
}
@Override
public boolean login() {
return this.loginApi.login();
}
}
2.3 测试验证
2.3.1 测试验证类
public class LoginServiceTest {
@Test
public void testLogin() {
//微信登录
LoginApi wxApi = new WxApi("https://mp.weixin.qq.com", "xxx");
LoginService wxLoginService = new LoginServiceImpl(wxApi);
wxLoginService.login();
//QQ登录
LoginApi qqApi = new QqApi("https://www.qq.com", "xxx");
LoginService qqLoginService = new LoginServiceImpl(qqApi);
qqLoginService.login();
}
}
2.3.2 测试结果
15:44:28.993 [main] INFO o.d.pattern.bridge.model.LoginApi - 微信登录成功
15:44:28.996 [main] INFO o.d.pattern.bridge.model.LoginApi - QQ登录成功
Process finished with exit code 0
四、桥接模式结构
- 抽象部分(Abstraction)提供高层控制逻辑,依赖于完成底层实际工作的实际对象。
- 实现部分(Implementation)为所有具体实现声明通用接口。抽象部分仅能通过这里声明的方法与实现对象交互。
抽象部分可以列出实现部分一样的方法,但是抽象部分通常声明一些复杂行为,这些行为依赖于多种由实现部分声明的操作。
- 具体实现(Concrete Implementations)中包括特定于平台的代码。
- 精确抽象(Refined Abstraction)提供控制逻辑的变体。与其父类一样,它们通过通用实现接口与不同的实现进行交互。
- 通常情况下,客户端(Client)仅关心如何与抽象部分合作。但是,客户端需要将抽象对象与一个实现对象连接起来。
设计模式并不难学,其本身就是多年经验提炼出的开发指导思想,关键在于多加练习,带着使用设计模式的思想去优化代码,就能构建出更合理的代码。
源码地址: https://github.com/yiyufxst/design-pattern-java参考资料:
小博哥重学设计模式:https://github.com/fuzhengwei/itstack-demo-design
深入设计模式:https://refactoringguru.cn/design-patterns/catalog