基础介绍

桥接(Bridge)是将抽象和实现解耦,让它们可以独立变化。 它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。 它是一种结构性设计模式,桥接模式是基于类的最小设计原则以及组合大于继承。桥接模式的用意是将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化。
意图:将抽象部分与实现部分分离,使它们都可以独立的变化。
主要解决:在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。
何时使用:实现系统可能有多个角度分类,每一种角度都可能变化。
如何解决:把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合。
注意事项:对于两个独立变化的维度,使用桥接模式再适合不过了。
image.png

案例说明

在游戏的设计中,多数都会有角色和武器之间的使用问题,比如人物:A、B和武器:刀、枪。
如果按照一般方法编写代码,我们会先设计人物基类,然后在人物的基类中定义武器使用的方法。接着使用人物A、B继承自人物基类,重写武器使用的方法,并且需要分别实现刀和枪的使用逻辑。
但是如果我们要增加新的人物和武器,这时候我们就会在每个新人物里都重写每个武器的实现方法。不仅如此,原先的人物A、B也要增加新武器的使用方法。
可见,这种设计思想对于程序的扩展是灾难级别的,并且违背了开闭原则。
我们最希望看到的扩展情况是什么样呢?
每次扩展新人物的时候,只关注人物本身,不涉及武器实现的编写。
每次扩展新武器的时候,只关注当前武器逻辑,不涉及与人物的关联。
当人物需要使用武器的时候,我们只对两者产生组合就好。

这时候我们再来看桥接模式的“抽象”与“实现”。
抽象代表的是角色抽象类。实现代表武器使用的实现部分。
我们目前的方式是在每个角色类(继承自角色基类)里实现武器的使用,也就是使用继承的方式去实现
我们希望武器的实现部分和和角色类分开实现,在角色和武器进行配对的时候,再把武器实现注入回去,也就是用组合的方式去实现。
回过头来看这句话:
“将抽象(角色类)与实现(武器使用逻辑)分离,使二者可以独立变化”,是不是就有些明朗了。
并且从上述例子可以看出,使用组合关系代替继承关系是桥接模式的对于此类问题的改进方式之一。

代码复用的基本方式:

继承:代码、概念复用(A is B的关系)
组合:代码复用(整体与部分的关系 A has B) 语法:ClassA{ B b;}

类与类的四大关系:

泛化:继承关系,耦合度最高
实现:实现类与接口之间的关系
关联:整体与部分的关系,另一个类作为当前类的成员出现
依赖:类与类的协作关系,另一个类作为当前类型方法的参数或返回值类型

结构与实现

设计模式-桥接Bridge - 图2

实现

  1. public class BridgeTest : MonoBehaviour
  2. {
  3. void Start()
  4. {
  5. Implementor imp = new ConcreteImplementorA();
  6. Abstraction abstraction = new RefinedAbstraction(imp);
  7. abstraction.Operation();
  8. }
  9. }
  10. //实现化角色
  11. public interface Implementor
  12. {
  13. void OperationImpl();
  14. }
  15. //具体实现化角色
  16. public class ConcreteImplementorA : Implementor
  17. {
  18. public void OperationImpl()
  19. {
  20. Debug.Log("具体实现化角色");
  21. }
  22. }
  23. //抽象化角色
  24. public abstract class Abstraction
  25. {
  26. protected Implementor imple;
  27. protected Abstraction(Implementor imple)
  28. {
  29. this.imple = imple;
  30. }
  31. public abstract void Operation();
  32. }
  33. //扩展抽象化角色
  34. public class RefinedAbstraction : Abstraction
  35. {
  36. public RefinedAbstraction(Implementor imple) : base(imple)
  37. {
  38. }
  39. public override void Operation()
  40. {
  41. Debug.Log("扩展抽象化角色");
  42. imple.OperationImpl();
  43. }
  44. //运行结果如下
  45. //扩展抽象化(Refined Abstraction)角色被访问
  46. //具体实现化(Concrete Implementor)角色被访问

应用场景

当一个类内部具备两种或多种变化维度时,使用桥接模式可以解耦这些变化的维度,使高层代码架构稳定。
桥接模式通常适用于以下场景。

  1. 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。
  2. 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
  3. 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。


    桥接模式的一个常见使用场景就是替换继承。我们知道,继承拥有很多优点,比如,抽象、封装、多态等,父类封装共性,子类实现特性。继承可以很好的实现代码复用(封装)的功能,但这也是继承的一大缺点。
    因为父类拥有的方法,子类也会继承得到,无论子类需不需要,这说明继承具备强侵入性(父类代码侵入子类),同时会导致子类臃肿。因此,在设计模式中,有一个原则为优先使用组合/聚合,而不是继承。