目录
- 背景
- 过程
- 总结
背景
现在要解决源码阶段的继承关系,无法在运行时改变从父类继承的实现。这里用的是手机品牌还有手机中的app,这种问题如何进行解决呢。这就要引入一个模式:桥接模式
过程
1、原则:合成/复用原则 :尽量使用合成/聚合原则,不要使用类继承。
2、定义:将抽象部分和它的实现分离,使他们都可以独立变化。(不是说抽象类和实现类分离,实现指的是抽象类和它的派生类用来实现自己的对象。)
3、例子:
如手机品牌和手机app的关系,他们可以是继承关系,也可以是关联关系。
如果是继承关系的话,是对象的继承关系在编译的时候定义好了,无法在运行时改变从父类继承的实现。子类的视线和它的父类有很紧密的依赖关系,父类实现中的任何变化必然会导致子类发生变化。于是出现了这种聚合关系。
- Abstraction抽象类
public abstract class Abstraction {
private Implementor imp;
//约束子类必须实现该构造函数
public Abstraction(Implementor imp) {
this.imp = imp;
}
public Implementor getImp() {
return imp;
}
//自身的行为和属性
public void request() {
this.imp.doSomething();
}
}
- Implementor抽象类
public abstract class Implementor {
public abstract void doSomething();
public abstract void doAnything();
}
- ConcreteImplementor
public class ConcreteImplementorA extends Implementor {
public void doSomething() {
System.out.println("具体实现A的doSomething执行");
}
public void doAnything() {
System.out.println("具体实现A的doAnything执行");
}
}
- RefinedAbstraction
public class RefinedAbstraction extends Abstraction {
//覆写构造函数
public RefinedAbstraction(Implementor imp) {
super(imp);
}
//修正父类行为
public void request() {
super.request();
super.getImp().doAnything();
}
}
- Client客户端
public class Client {
public static void main(String[] args) {
Implementor imp = new ConcreteImplementorA();
Abstraction abs = new RefinedAbstraction(imp);
abs.request();
}
}
二、桥接模式的应用
1. 何时使用
系统可能有多个角度分类,每一种角度都可能变化时
2. 方法
把这种角度分类分离出来,让它们单独变化,减少它们之间的耦合(合成/聚合复用原则)
3. 优点
抽象和实现分离。桥梁模式完全是为了解决继承的缺点而提出的设计模式
优秀的扩展能力
实现细节对客户透明。客户不用关心细节的实现,它已经由抽象层通过聚合关系完成了封装
4. 缺点
会增加系统的理解与设计难度。由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程
5. 使用场景
不希望或不适用使用继承的场景
接口或抽象类不稳定的场景
重用性要求较高的场景
6. 应用实例
开关。我们可以看到的开关是抽象的,不用管里面具体怎么实现
手机品牌与手机软件。两者间有一条聚合线,一个手机品牌可以有多个手机软件
7. 注意事项
不要一涉及继承就考虑该模式,尽可能把变化的因素封装到最细、最小的逻辑单元中,避免风险扩散
当发现类的继承有n层时,可以考虑使用该模式.
三、桥接模式的实现
下面我们举一个例子,就拿上面说的手机品牌与手机软件为例,我们可以让手机既可以按照手机品牌来分类,也可以按手机软件来分类。由于实现的方式有多种,桥接模式的核心意图就是把这些实现独立出来,让它们各自地变化,这就使得没中实现的变化不会影响其他实现,从而达到应对变化的目的。
- 手机品牌抽象类
public abstract class HandsetBrand {
protected HandsetSoft soft;
//设置手机软件
public void setHandsetSoft(HandsetSoft soft) {
this.soft = soft;
}
//运行
public abstract void run();
}
- 手机软件抽象类
public abstract class HandsetSoft {
public abstract void run();
}
- 各类手机品牌
public class HandsetBrandA extends HandsetBrand {
public void run() {
soft.run();
}
}
- 各类手机软件
public class HandsetGame extends HandsetSoft {
public void run() {
System.out.println("运行手机游戏");
}
}
5. Client客户端
public class Client {
public static void main(String[] args) {
HandsetBrand ab;
//使用A品牌手机
ab = new HandsetBrandA();
System.out.println("A品牌手机:");
ab.setHandsetSoft(new HandsetGame());
ab.run();
ab.setHandsetSoft(new HandsetAddressList());
ab.run();
//分隔符
System.out.println("---------------");
//使用B品牌手机
ab = new HandsetBrandB();
System.out.println("B品牌手机:");
ab.setHandsetSoft(new HandsetGame());
ab.run();
ab.setHandsetSoft(new HandsetAddressList());
ab.run();
}
}
总结
这个桥接模式最深刻的一个点是:将源码定义好的关系修改为运行时在客户端中指定关系。解开源码定义好的类之间的耦合,变成运行的时候才让类之间耦合起来。
这样我现在如果想要增加一个功能,比如音乐播放器,那么只有增加这个类就可以了,不会影响到其他任何类,类的个数增加也只是一个;如果是要增加S品牌,只需要增加一个品牌的子类就可以了,个数也是一个,不会影响到其他类。这显然符合开放-封闭原则。
而这里用到的合成/聚合复用原则是一个很有用处的原则,即优先使用对象的合成或聚合,而不是继承。究其原因是因为继承是一种强耦合的结构,父类变,子类就必须变。