引入问题:
假如我们需要开发一个同事支持PC和手机的坦克游戏,游戏在PC和手机上功能都一样,都有同样的类型,面临同样的功能需求变化,比如坦克可能有多种不同的型号:T50,T75,T90...
但是PC和手机上的图形绘制、声效、操作等实现完全不同...因此对于各种型号的坦克,都要提供不同平台上的坦克实现。
动机(Motivation)
思考上述问题的症结:事实上由于Tank类型的固有逻辑,使得Tank类型具有了两个变化的维度---一个变化的维度为“平台的变化”,一个变化的维度为“型号的变化”。
如何应对这种“多维度的变化”?如何利用面向对象技术来使得Tank类型可以轻松地沿着“平台”和“型号”两个方向变化,而不引入额外的复杂度?
意图(Intent)
将抽象部分与实现部分分离,使它们都可以独立的变化。
结构(Structure)
代码实现:
1 一种维度的变化 2 public abstract class TankPlatformImplementation 3 { 4 public abstract void MoveTo(); 5 public abstract void DrawTank(); 6 public abstract void DoShot(); 7 } 8 9 public class PCTankImplementation : TankPlatformImplementation 10 { 11 12 public override void MoveTo() 13 { 14 Console.WriteLine("PC端移动"); 15 } 16 17 public override void DrawTank() 18 { 19 Console.WriteLine("PC端画坦克"); 20 } 21 22 public override void DoShot() 23 { 24 Console.WriteLine("PC端攻击"); 25 } 26 } 27 28 public class MobileTankImplementation : TankPlatformImplementation 29 { 30 31 public override void MoveTo() 32 { 33 Console.WriteLine("移动端移动"); 34 } 35 36 public override void DrawTank() 37 { 38 Console.WriteLine("移动端画坦克"); 39 } 40 41 public override void DoShot() 42 { 43 Console.WriteLine("移动端攻击"); 44 } 45 }
1 另一种维度的变化 2 public abstract class Tank 3 { 4 protected TankPlatformImplementation tpimp; 5 public abstract void Run(); 6 public abstract void Stop(); 7 } 8 9 public class T50 : Tank 10 { 11 public T50(TankPlatformImplementation tpimp) 12 { 13 this.tpimp = tpimp; 14 } 15 public void MoveTo() 16 { 17 Console.Write("T50的"); 18 tpimp.MoveTo(); 19 } 20 public void DoShot() 21 { 22 Console.Write("T50的"); 23 tpimp.DoShot(); 24 } 25 public void DrawTank() 26 { 27 Console.Write("T50的"); 28 tpimp.DrawTank(); 29 } 30 31 32 public override void Run() 33 { 34 Console.WriteLine("T50跑"); 35 } 36 37 public override void Stop() 38 { 39 Console.WriteLine("T50停"); 40 } 41 } 42 public class T75 : Tank 43 { 44 public T75(TankPlatformImplementation tpimp) 45 { 46 this.tpimp = tpimp; 47 } 48 public void MoveTo() 49 { 50 Console.Write("T75的"); 51 tpimp.MoveTo(); 52 } 53 public void DoShot() 54 { 55 Console.Write("T75的"); 56 tpimp.DoShot(); 57 } 58 public void DrawTank() 59 { 60 Console.Write("T75的"); 61 tpimp.DrawTank(); 62 } 63 64 public override void Run() 65 { 66 Console.WriteLine("T75跑"); 67 } 68 69 public override void Stop() 70 { 71 Console.WriteLine("T75停"); 72 } 73 } 74 public class T90 : Tank 75 { 76 public T90(TankPlatformImplementation tpimp) 77 { 78 this.tpimp = tpimp; 79 } 80 public void MoveTo() 81 { 82 Console.Write("T90的"); 83 tpimp.MoveTo(); 84 } 85 public void DoShot() 86 { 87 Console.Write("T90的"); 88 tpimp.DoShot(); 89 } 90 public void DrawTank() 91 { 92 Console.Write("T90的"); 93 tpimp.DrawTank(); 94 } 95 96 public override void Run() 97 { 98 Console.WriteLine("T90跑"); 99 } 100 101 public override void Stop() 102 { 103 Console.WriteLine("T90停"); 104 } 105 }
1 主函数的调用 2 static void Main(string[] args) 3 { 4 T75 tank = new T75(new MobileTankImplementation()); 5 tank.DrawTank(); 6 tank.DoShot(); 7 tank.MoveTo(); 8 tank.Run(); 9 tank.Stop(); 10 Console.ReadKey(); 11 }
Bridge模式的几个要点:
Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象(Tank的型号)和实现(不同的平台)可以沿着各自的维度来变化。
所谓抽象和实现沿着各自的维度的变化,即“子类化”它们,比如不同的Tank型号子类,和不同的平台子类。得到各个子类之后,便可以任意组合它们,从而获得 不同平台上的不同型号。
Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性较差。Bridge模式是比多继承方案更好的解决方法。
Bridge模式的应用一般在“两个非常强的变化维度”,有时候即使有两个变化的维度,但是某个方向的变化维度并不剧烈--换言之两个变化不会导致纵横交错的结果,并不一定要使用Bridge模式。