23种设计模式之桥接模式
参考资料
- Java设计模式:23种设计模式全面解析(超级详细)
- 韩顺平老师的Java设计模式(图解+框架源码剖析)
- 秦小波老师的《设计模式之禅》
下文如有错漏之处,敬请指正
一、简介
定义
将抽象部分与它的行为部分实现分离,保持各个部分的独立性和其功能的可扩展性
抽象:对事物的抽象 (名词)
接口:对行为的抽象 (动词)
特点
- 桥接模式是一种结构型设计模式
通用类图
桥接模式的主要角色:
-
Abstraction
抽象化角色
定义行为,同时保存一个对实现化角色的引用,该角色一般是抽象类。
-
Implementor
实现化角色
它是接口或者抽象类,定义角色必需的行为和属性。
-
RefinedAbstraction
扩充抽象化角色
它引用实现化角色对抽象化角色进行扩充。
-
ConcreteImplementor
具体实现化角色
它实现接口或抽象类定义的方法和属性。
优点
-
抽象和实现分离
这也是桥梁模式的主要特点,它完全是为了解决继承的缺点而提出的设计模式。在该模式下,实现可以不受抽象的约束,不用再绑定在一个固定的抽象层次上。
-
优秀的扩充能力
想增加实现?没问题!想增加抽象也没有问题!只要对外暴露的接口层允许这样的变化。
-
实现细节对客户透明
客户不用关心细节的实现,它已经由抽象层通过聚合关系完成了封装。
应用场景
-
不希望或不适用使用继承的场景
例如继承层次过渡、无法更细化设计颗粒等场景,需要考虑使用桥梁模式。
-
重用性要求较高的场景
设计的颗粒度越细,则被重用的可能性就越大,而采用继承则受父类的限制,不可能出 现太细的颗粒度。
-
JDBC中驱动程序(Driver和Connection通过DriverManager类进行桥接)
二、桥接模式
现在有个手机抽象类,抽象类里包括品牌,功能;品牌有小米手机,苹果手机;功能有打电话、发短信。创建两个品牌的手机并赋予他们功能。
抽象化角色:
package bridge;
public abstract class Phone {
// 定义对实现化角色的引用
Implementor implementor;
// 自身的属性
Brand brand;
// 约束子类必须实现该构造函数
public Phone(Implementor implementor,Brand brand) {
this.implementor = implementor;
this.brand=brand;
}
// 自身的行为
public void run(){
this.implementor.run();
}
public Implementor getImplementor() {
return implementor;
}
public void setImplementor(Implementor implementor) {
this.implementor = implementor;
}
public String gerBrandInfo(){
return this.brand.getBrand();
}
}
扩充抽象化角色:
package bridge;
public class XiaoMi extends Phone{
public XiaoMi(Implementor implementor,Brand brand) {
super(implementor, brand);
}
}
package bridge;
public class Iphone extends Phone {
public Iphone(Implementor implementor,Brand brand) {
super(implementor,brand );
}
}
实现化角色:
package bridge;
public interface Implementor {
public void run();
}
具体实现化角色
package bridge;
public interface Call extends Implementor{
public void call();
}
package bridge;
public interface sendMessage extends Implementor {
public void sendMessage();
}
具体功能接口实现类
package bridge;
public class CallImpl implements Call {
@Override
public void call() {
System.out.println("正在打电话……");
}
@Override
public void run() {
call();
}
}
package bridge;
public class sendMessageImpl implements sendMessage {
@Override
public void sendMessage() {
System.out.println("正在发短信……");
}
@Override
public void run() {
sendMessage();
}
}
品牌类
package bridge;
public class Brand {
protected String brand;
public Brand(String brand) {
this.brand = brand;
}
public String getBrand() {
return brand;
}
}
Client:
package bridge;
public class Client {
public static void main(String[] args) {
// 定义一个实现化角色
Implementor implementor = new CallImpl();
// 定义一个抽象化角色
Phone phone = new XiaoMi(implementor, new Brand("Iphone"));
System.out.println("品牌是:" + phone.gerBrandInfo());
// 运行具体功能
phone.run();
phone.setImplementor(new sendMessageImpl());
phone.run();
System.out.println("======================");
phone = new XiaoMi(implementor, new Brand("XiaoMi"));
System.out.println("品牌是:" + phone.gerBrandInfo());
// 运行具体功能
phone.run();
phone.setImplementor(new sendMessageImpl());
phone.run();
/**
* 输出结果:
* 品牌是:Iphone
* 正在打电话……
* 正在发短信……
* ======================
* 品牌是:XiaoMi
* 正在打电话……
* 正在发短信……
*/
}
}
当增加一个新品牌时,只需要增加一个品牌类(继承抽象化角色),就可以复用打电话和发短信的功能,不改变原有结构。当增加一个新功能时,只需要增加一个新功能接口继承实现化角色并实现新功能即可,不改变原有结构。
三、总结
- 桥梁模式是非常简单的,使用该模式时主要考虑如何拆分抽象和实现,并不是一涉及继 承就要考虑使用该模式,那还要继承干什么呢?桥梁模式的意图还是对变化的封装,尽量把可能变化的因素封装到最细、最小的逻辑单元中,避免风险扩散。因此读者在进行系统设计时,发现类的继承有N层时,可以考虑使用桥梁模式。
- 继承的优点有很多,可以把公共的方法或属性抽取,父类封装共性,子类实现特性,这是继承的基本功能。缺点就是强侵入。而桥梁模式描述了类间弱关联关系,子类要拥有这个方法很简单,桥梁搭过去,获得这个方法,不想使用这个方法也没关系,对子类不产生影响。
- 我们可以扬长避短,对于比较明确不发生变化的,则通过继承来完成;若不能确定是否会发生变化的,那就认为是会发生变化,则通过桥梁模式来解决。