桥接模式(Bridge):将抽象部分与它的实现部分分离,使它们都可以独立底变化。
UML图:
package com.thpin.repository.designpattern;
public class BridgeDemo {
public static void main(String[] args) {
Abstraction abstractionA = new RefinedAbstractionA();
abstractionA.setAbstraction(new ConcreteImplementorA());
abstractionA.opteration();
Abstraction abstractionB = new RefinedAbstractionB();
abstractionB.setAbstraction(new ConcreteImplementorB());
abstractionB.opteration();
// A可以执行B,B也可以执行A。觉得并不合理,桥接侧重的是扩展,而不在于通用
abstractionA.setAbstraction(new ConcreteImplementorB());
abstractionA.opteration();
abstractionB.setAbstraction(new ConcreteImplementorA());
abstractionB.opteration();
}
}
/*
* 实现接口
*/
abstract class Implementor {
public abstract void operation();
}
/*
* 具体实现A
*/
class ConcreteImplementorA extends Implementor {
public void operation() {
System.out.println("具体实现A的方法执行");
}
}
/*
* 具体实现B
*/
class ConcreteImplementorB extends Implementor {
public void operation() {
System.out.println("具体实现B的方法执行");
}
}
/*
* 抽象接口
*/
abstract class Abstraction {
protected Implementor implementor;
public void setAbstraction(Implementor implementor) {
this.implementor = implementor;
}
public void opteration() {
implementor.operation();
}
}
/*
* 具体抽象A
*/
class RefinedAbstractionA extends Abstraction {
public void opteration() {
implementor.operation();
}
}
/*
* 具体抽象B
*/
class RefinedAbstractionB extends Abstraction {
public void opteration() {
implementor.operation();
}
}
结果:
具体实现A的方法执行
具体实现B的方法执行
具体实现B的方法执行
具体实现A的方法执行
合成(Composition)/聚合(Aggretation)原则,尽量使用合成/聚合,尽量不要使用继承(extend)
对像的继承关系是在编译期间就定义好的,所以无法在运行期间改变从父类继承的实现。子类实现与其父类有紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。当你需要复用子类时,如果继承下来的实现不适合解决新的问题,父类必须重写或者由更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。继承是对象关系中耦合性最强的。
而聚合表示一种弱的‘拥有‘关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分(A和B的关系就是集合和元素的关系);合成是一种强的‘拥有’关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样(A和B的关系是对象和对象的内部属性的关系)。合成和聚合的耦合性远低于继承的耦合性,A聚合B和A合成B,A发生变化时B不一定随之变化,而B继承A的话,A变化B一定随之变化,这就是为什么说继承是强耦合的原因。在设计过程中要避免强耦合的继承关系,如果可以的话。
优先使用对象的合成/聚合将有助于保持每个类被封装,实现被集中在单个的任务上。这样功能被水平展开减小继承的深度,使得类和类的继承层次保持在较小规模,并且不太可能增长为不可控的庞然大物。
桥接模式就是利用合成/聚合关系替换继承关系,将抽象和和实现分离,这里说的分离是指抽象类有自己的一套继承体系,实现类也有自己的一套继承体系。就像手机品牌和手机软件之间的关系一样,如果一味的采用继承来设计,就会是手机-品牌手机-品牌手机的软件 这种三层结构的继承关系,或者品牌和软件调换,最终手机的品牌和软件是强耦合在一起,而且数据的机构是垂直分布的,每层之前存在着千丝万缕的关联。而如果用合成/聚合的关系,就是这样的手机-品牌手机、软件-品牌手机的软件,软件是手机的一个属性,这样就将原来的三层接口拆解成两个子系统。
策略模式UML图:
仔细观察桥接模式和策略模式非常像,但还是略有不同,策略模式中的策略类是一个final类,也就是说没有子类,它起的作用就是直接选择它的属性算法对象来间接决定自己拥有什么算法,而桥接模式中与策略类对应的是抽象类,它是可以继续被继承拥有多个子类的。也就是说策略控制对算法的选择,而桥接是控制实现类和抽象类的组合。桥接模式比策略模式更复杂一些,但两个设计模式约而同的都选择了合成/聚合的方式,这也说明遵守设计的原则设计出的代码结构也许就是一个设计模式,而继续遵从设计原则稍作改动可能就产生了另外一种设计模式。两者的区别可以详细阅读这篇文章点击桥接模式和策略模式的区别
桥接模式侧重的是扩展和修改,而对于抽象类和实现类的原有组合是很少改动的,只是会根据需求添加各自的子类,然后再组合。那么仔细观察上面的代码,不难发下,抽象类的属性可以被实现类随意替换,而不会出现任何问题,在这一点上来说桥接模式不应该拥有这个功能,这样会给客户端造成困难,实现类的子类可以被任何抽象类的子类来使用。这是本人的观点,网上查阅没有找到相关方面的论述,希望和大家一起讨论提高。下面的代码是对桥接模式的改进,在实现类接口和子类之间添加一个抽象层,而抽象类的属性选择就可以由这一层来控制,避免了随意组合的现象发生。就像鸟类的子类大雁只能合成大雁的不能去合成小燕子的翅膀。
package com.thpin.repository.designpattern;
public class BridgeDemo2 {
public static void main(String[] args) {
AndroidSoftware androidSoftware = new AndroidGame();
IOSSoftware iosSoftware = new IOSGame();
PhoneOS android = new AndroidOS(androidSoftware);
PhoneOS ios = new IOS(iosSoftware);
// 把下面两行注释代码打开会报错,不是想要的参数类型
// android = new AndroidOS(iosSoftware);
// ios = new IOS(androidSoftware);
android.run();
ios.run();
}
}
/*
* 软件接口
*/
interface Software {
void operation();
}
/*
* Android软件抽象类
*/
abstract class AndroidSoftware implements Software {
}
/*
* Android游戏软件
*/
class AndroidGame extends AndroidSoftware {
public void operation() {
System.out.println("Android game");
}
}
/*
* IOS软件抽象类
*/
abstract class IOSSoftware implements Software {
}
/*
* IOS游戏软件
*/
class IOSGame extends IOSSoftware {
public void operation() {
System.out.println("IOS game");
}
}
/*
* 操作系统抽象类
*/
abstract class PhoneOS {
protected Software software;
public PhoneOS(Software software) {
this.software = software;
}
public void run() {
software.operation();
}
}
/*
* IOS操作系统
* 构造方法和set方法参数限定为IOSSoftware抽象类
*/
class IOS extends PhoneOS {
public IOS(IOSSoftware software) {
super(software);
}
public void setSoftware(IOSSoftware software) {
this.software = software;
}
}
/*
* Android操作系统
* 构造方法和set方法参数限定为AndroidSoftware抽象类
*/
class AndroidOS extends PhoneOS {
public AndroidOS(AndroidSoftware software) {
super(software);
}
public void setSoftware(AndroidSoftware software) {
this.software = software;
}
}
结果:
Android game
IOS game
经过改造,IOS操作系统只能运行IOS软件,安卓操作系统只能运行安卓软件,想安装都安装不上。这样会不会更好一些呢?