1. 认识桥接模式
1. 定义:将抽象与实现分离,使它们可以独立变化。用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。也就是说,通常我们会首先定义一个抽象化接口,然后再创建一个具体实现类继承该接口,完成具体的功能实现。但是使用桥接后,我们会将这两部分隔离,具体的功能实现类的一套接口以及实现类,以及定义所需功能的抽象类以及抽象实现类。抽象类定义所需功能,抽象实现类继承抽象类,并且将功能实现类对象作为域对象,然后再抽象类定义的所需方法接口中调用功能实现类对象的方法。
2. 模式的组成结构:
- 抽象化(Abstraction)角色:定义抽象类,并包含一个对具体实现对象的引用。
- 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用具体实现角色中的业务方法。
- 具体实现(Implementor)角色接口:定义具体实现角色的接口,接口中实现的功能是原本应该在扩展抽象化类中实现的,但独立出来通过组合关系供扩展抽象化角色调用。
- 具体实现(Concrete Implementor)角色:给出具体实现角色接口的具体实现。
3. 参考代码实现:
public class BridgeTest
{
public static void main(String[] args)
{
Implementor imple=new ConcreteImplementorA();
Abstraction abs=new RefinedAbstraction(imple);
abs.Operation();
}
}
//实现化角色
interface Implementor
{
public void OperationImpl();
}
//具体实现化角色
class ConcreteImplementorA implements Implementor
{
public void OperationImpl()
{
System.out.println("具体实现化(Concrete Implementor)角色被访问" );
}
}
//抽象化角色
abstract class Abstraction
{
protected Implementor imple;
protected Abstraction(Implementor imple)
{
this.imple=imple;
}
public abstract void Operation();
}
//扩展抽象化角色
class RefinedAbstraction extends Abstraction
{
protected RefinedAbstraction(Implementor imple)
{
super(imple);
}
public void Operation()
{
System.out.println("扩展抽象化(Refined Abstraction)角色被访问" );
imple.OperationImpl();
}
}
2. 理解桥接模式
1. 应用场景:
- 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。比如台式机电脑的组装,其中的主要部件几乎都是可以互换的,主板、显卡、cpu都是可以更换的。
- 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
- 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。
2. 举例:对于一个台式机的组装来说,其主要组件包括处理器芯片、硬盘以及显示器等,芯片提供数据计算功能、硬盘提供数据存储功能、显示器提供数据显示功能,而台式机则会将这些组件组合起来,实现一个完整的计算机。
但注意芯片、硬盘、显示器都是可以随意更换的,如果每一个计算机类中的芯片计算方法、数据显示方法、数据存储方法都是在电脑描述类中进行实现的话(继承芯片接口、硬盘接口和显示器接口),一旦想要更改某个部件的具体实现方法,就必须要修改计算机类或者重新创建一个计算机类,不符合开闭原则,。
所以应该通过桥接模式,以组合的方式实现。桥接模式中的抽象化角色就相当于主板的作用,主板规定了所需要的功能接口以及组件,功能接口的具体实现由组件提供,同样的,主板也是可以更换的,所以说桥接模式是将抽象与实现分离的一种模式。
3. 桥接模式与适配器模式区别:两者确实很相似,如果桥接模式中的变化维度(抽象化角色中包含的具体实现角色数量)只有一个的话,那实际上就是适配器模式,但是桥接模式是应用于多个变化维度的。比如说一个电脑类(抽象化角色),其中包括显卡、主板和CPU等(具体实现角色),每一个部件都有多种类可以更换,电脑类也需要这些组件所提供的功能,但不可能继承这些接口去实现,因为一旦需要更换某个组件就需要更改电脑类,不符合开闭原则,而且代价过高,不利于复用。
也就是说,适配器模式指适用于只有一个变化维度的情况,如果有多个变化维度,就是桥接模式。
合成复用原则(Composite Reuse Principle,CRP)又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)。它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。组合关系带来的耦合性是低于继承关系的。
通常类的复用分为继承复用和合成复用两种,继承复用虽然有简单和易实现的优点,但它也存在以下缺点,所以几乎不使用。
- 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。
- 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
- 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。
采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,通常都使用该方式,它有以下优点。
- 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。
- 新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。
- 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。