桥接模式的动机:
由于某些类型的固有的实现逻辑,使得它们具有两个乃至多个变化的维度。
如何应对这种“多维度的变化”?如何利用面向对象技术使得类型可以轻松地沿着两个乃至多个方向变化,而不引入额外的复杂度?
场景描述:
● 手机都有通讯录和游戏功能,M品牌手机和N品牌手机都有通讯录的增删改查功能和游戏功能。可以有两种结构
结构1:
◇ 父亲是‘手机品牌’,下有‘手机品牌M’和‘手机品牌N’,每个子类下各有‘通讯录’和‘游戏’子类。
结构2:
◇ 父类时‘手机软件’,下有‘通讯录’和‘游戏’子类,每个子类下各有‘手机品牌M’和‘手机品牌N’。
结构1:
结构2:
● 在面向对象设计中,有一个很重要的设计原则:
合成/聚合复用原则。即优先使用对象合成/聚合,而不是类继承。
● 合成/聚合复用原则的好处是,优先使用对象的合成/聚合有助于保持每个类并封装,并集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。
问题:
● Q:如果现在需要每个品牌都增加一个音乐播放功能,如何做?
● A:结构1要求在每个品牌的下面都增加一个子类;结构2要求在每个手机软件下面增加一个音乐播放子类,在这个子类下为每个品牌增加音乐播放功能。这两个子类的差别不大,但是因额为品牌不同,增加功能就不太容易。
● Q:现在又来了一家新的手机品牌‘S’,它也有游戏、通讯录、音乐播放功能,如何处理?
● A:那就得在增加‘手机品牌S’类和三个下属功能子类。
这就很麻烦了,如果还需要增加‘输入法’功能、‘拍照’功能,在增加‘L品牌’、‘L品牌’类非常困难。
● 上面的问题,需要用对象的职责,而不是结构来考虑问题。
● 实际上,‘游戏’、‘通讯录’、‘音乐播放’这些功能都是软件,让其分离与手机的耦合,就大大减少面对新需求时改动过大的不合理情况。
● 应该有个‘手机品牌’抽象类和‘手机软件’抽象类,让不同的品牌和功能都分别继承于它们,这样要增加新的品牌或新的功能就不用影响其它类了。
结构图:
手机品牌和手机软件之间的关系是手机品牌包含有手机软件,但软件并不是品牌的一部分,所以它们之间是聚合关系。
基本代码:
//手机软件
abstract class HandsetSoft
{
public abstract void Run();
}
//手机品牌
abstract class HandsetBrand
{
protected HandsetSoft soft;
//设置手机软件
public void SetHandsetSoft(HandsetSoft soft)
{
this.soft = soft;
}
//运行
public abstract void Run();
}
class HandsetBrandM extends HandsetBrand//手机品牌M
{
public void Run()
{
System.out.println("M Brand");
soft.Run();
}
}
class HandsetBrandN extends HandsetBrand//手机品牌N
{
public void Run()
{
System.out.println("N Brand");
soft.Run();
}
}
class HandsetGame extends HandsetSoft//手机游戏
{
public void Run()
{System.out.println("运行手机游戏");}
}
class AddressList extends HandsetSoft//手机通讯录
{
public void Run()
{System.out.println("运行手机通讯录");}
}
class Mp3 extends HandsetSoft//手机mp3
{
public void Run()
{System.out.println("运行手机mp3");}
}
客户端:
public class CellPhonePattern
{
public static void main(String[] args)
{
HandsetBrand ab;
ab = new HandsetBrandN();
ab.SetHandsetSoft(new HandsetGame()); ab.Run();
ab.SetHandsetSoft(new AddressList()); ab.Run();
ab = new HandsetBrandM();
ab.SetHandsetSoft(new HandsetGame()); ab.Run();
ab.SetHandsetSoft(new AddressList()); ab.Run();
ab.SetHandsetSoft(new Mp3()); ab.Run();
}
}
解决问题:
● 如果要增加一个功能,比如拍照功能,在手机软件部分增加手机拍照子类。类的个数增加也只是一个。不会影响其他任何类。
● 如果要增加S品牌,在手机品牌部分增加一个品牌子类就可以了。个数也是一个,不会影响其他类。
代码实现:
在手机软件部分添加一个手机拍照类
//手机拍照
class photo extends HandsetSoft
{
public void Run()
{System.out.println("运行手机牌照");}
}
在手机品牌部分添加一个手机品牌S类
//手机品牌S
class HandsetBrantS extends HandsetBrand
{
public void Run()
{
System.out.println("S Brand");
soft.Run();
}
}
客户端增加:
ab = new HandsetBrandS();
ab.SetHandsetSoft(new HandsetGame()); ab.Run();
ab.SetHandsetSoft(new AddressList()); ab.Run();
ab.SetHandsetSoft(new Mp3()); ab.Run();
ab.SetHandsetSoft(new photo()); ab.Run();
这样的设计符合开放-封闭原则,不会修改原来的代码,而只是扩展类就行了。
合成/聚合复用原则,优先使用对象的合成或聚合,而不是类继承。
桥接模式:
● 可以看出,这个系统含有两个等级结构:
◇ 由抽象化角色和提炼抽象化角色组成的抽象化等级结构。
◇ 由实现化角色和两个具体实现化角色所组成的实现化等级结构。
基本代码:
//Implementor类
abstract class Implementor
{
public abstract void Operation();
}
public class ConcreteImplementorA extends Implementor
{
public void Operation()
{
System.out.println("具体实现A的方法执行");
}
}
public class ConcreteImplementorB extends Implementor
{
public void Operation()
{
System.out.println("具体实现B的方法执行");
}
}
//Abstraction类
public class Sbstraction
{
protected Implementor implementor;
public void SetImplementor(Implementor implementor)
{this.implementor = implementor;}
public void Operation()
{implementor.Operation();}
}
//RefinedAbstraction类
public class RefinedAbstraction extends Abstraction
{
public void Operation()
{
implementor.Operation();
}
}
//客户端调用代码
public class client
{
public static void main(String[] args)
{
Abstraction ab = new RefinedAbstraction();
ab.SetImplementor(new ConcreteImplementorA());
ab.Operation();
ab.SetImplementor(new ConcreteImplementorB());
ab.Operation();
}
}
具体实现A的方法执行;
具体实现B的方法执行。
本质:分离抽象和实现。