Strategy 策略模式
策略模式:定义一系列的算法,把他们一个个封装起来,并且使它们可以相互替换,。本模式使得算法可以独立于使用它的客户而变化。
故事背景:开发一款鸭子模拟游戏,游戏中会出现各种鸭子,一边游泳,一边嘎嘎叫。此系统的内部使用了标准的OO技术,设计了一个鸭子基类,并让各种鸭子继承此基类。基类中有Speak()方法,Swim()方法,还有一个虚方法Display()。3个子类WhiteDuck,BlackDuck,RubberDuck
代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace StrategyPattern 8 { 9 public class Duck 10 { 11 protected string name = "duck"; 12 public Duck() 13 { 14 15 } 16 public Duck(string name) 17 { 18 this.name = name; 19 } 20 public void Quack() 21 { 22 Console.WriteLine(name + "鸭子在嘎嘎叫"); 23 } 24 public void Swim() 25 { 26 Console.WriteLine(name + "鸭子在游泳"); 27 } 28 29 public virtual void Display() 30 { 31 Console.WriteLine(name + "基类的display方法被调用"); 32 } 33 public void Fly() 34 { 35 Console.WriteLine(name + "鸭子在飞"); 36 } 37 } 38 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace StrategyPattern 8 { 9 class RedDuck :Duck 10 { 11 public RedDuck() 12 { 13 14 } 15 public RedDuck(string name):base(name) 16 { 17 18 } 19 public override void Display() 20 { 21 22 Console.WriteLine(name + "红头鸭子"); 23 } 24 } 25 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace StrategyPattern 8 { 9 class GreenDuck:Duck 10 { 11 public GreenDuck() 12 { 13 14 } 15 public GreenDuck(string name):base(name) 16 { 17 18 } 19 public override void Display() 20 { 21 Console.WriteLine(name + "绿头鸭子"); 22 } 23 } 24 }
using System; namespace StrategyPattern { class RubberDuck:Duck { public RubberDuck() { } public RubberDuck(string name):base(name) { } public override void Display() { Console.WriteLine(name + "橡皮鸭子"); } } }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace StrategyPattern 8 { 9 class Client 10 { 11 static void Main(string[] args) 12 { 13 Duck redDuck = new RedDuck("Mr.Red"); 14 redDuck.Display(); 15 redDuck.Quack(); 16 redDuck.Swim(); 17 18 Duck greenDuck = new GreenDuck("Mr.Green"); 19 greenDuck.Display(); 20 greenDuck.Quack(); 21 greenDuck.Swim(); 22 23 Duck rubberDuck = new RubberDuck("Mr.Rubber"); 24 rubberDuck.Display(); 25 rubberDuck.Quack(); 26 rubberDuck.Swim(); 27 28 Console.ReadKey(); 29 } 30 } 31 }
运行结果:
新的需求:现在需要会飞的鸭子
实现:只要在基类中新加一个Fly()方法,然后在客户端调用就可以了。真的是这样么?如果这样做,你会RubberDuck在飞(注:rubber 橡皮),这可不是我们想要的结果。
要想解决这个办法很简单,在RubberDuck中定义一个同名函数隐藏基类中的同名方法Fly,然后什么都不做就行了。或者把Fly设计成一个虚方法,同样在子类中覆盖,什么都不做就行了。
新的需求:需要诱饵鸭子,既不会飞,也不会叫。解决这个问题的方法也很简单,在定义一个子类DecoyDuck,覆盖子类的Fly和Speak方法就行了,同样方法体为空。
思考:利用继承来提供Duck的行为导致的缺点:
1.运行时的行为不容易改变
2.改变会牵一发而动全身,造成其他鸭子不想要的改变。
3.代码在多个子类中重复
4.很难直到所有鸭子的全部行为。
策略模式大显身手:
把行为与Duck分离出来,定义两个接口QuackBehaviour和FlyBehaviour。定义两个实现QuackBehaviour的类:QuackLoudly和QuackMute。定义两个实现FlyB ehaviour的类:FlyWithWings和FlyNoway。
Duck中删除Fly和Quack方法,添加PerformQuack和PerformFly方法。要想获得叫和飞的行为只要获得飞和叫的类就可以了,让飞和叫的类取实现这些功能。因此需要在Duck中添加两个接口的引用:
QuackBehaviour quackBehaviour 和 FlyBehaviour flyBehaviour。获得引用还不行,还需要获得实例。因此再添加2个方法:SetQuackBehaviour(QuackBehaviour qb) 和 SetFlyBehaviour(FlyBehaviour fb);
具体代码如下,2个具体的鸭子类就可已说明问题了,偷懒起见GreenDuck就不测试了。
1 using System; 2 namespace StrategyPattern 3 { 4 public interface FlyBehaviour 5 { 6 void Fly(); 7 } 8 public class FlyWithWings : FlyBehaviour 9 { 10 public void Fly() 11 { 12 Console.WriteLine("我会飞"); 13 } 14 } 15 public class FlyNoWay:FlyBehaviour 16 { 17 public void Fly() 18 { 19 Console.WriteLine("我不会飞"); 20 } 21 } 22 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace StrategyPattern 8 { 9 public interface QuackBehaviour 10 { 11 void Quack(); 12 } 13 14 public class QuackLoudly:QuackBehaviour 15 { 16 public void Quack() 17 { 18 Console.WriteLine(("嘎嘎叫")); 19 } 20 } 21 public class QuackMute:QuackBehaviour 22 { 23 public void Quack() 24 { 25 Console.WriteLine("不会叫"); 26 } 27 28 } 29 }
1 using System; 2 3 namespace StrategyPattern 4 { 5 public class Duck 6 { 7 public FlyBehaviour flyBehaviour; 8 public QuackBehaviour quackBehaviour; 9 protected string name = "duck"; 10 public Duck() 11 { 12 13 } 14 public Duck(string name) 15 { 16 this.name = name; 17 } 18 public void SetFlyBehaviuor(FlyBehaviour fb) 19 { 20 flyBehaviour = fb; 21 } 22 public void SetQuackBehaviour(QuackBehaviour qb) 23 { 24 quackBehaviour = qb; 25 } 26 27 public void Swim() 28 { 29 Console.WriteLine(name + "鸭子在游泳"); 30 } 31 public void PerformQuack() 32 { 33 Console.WriteLine(("I am" + name)); 34 quackBehaviour.Quack(); 35 } 36 public virtual void Display() 37 { 38 Console.WriteLine(name + "基类的display方法被调用"); 39 } 40 public void PerformFly() 41 { 42 Console.WriteLine(("I am" + name)); 43 flyBehaviour.Fly(); 44 } 45 } 46 }
测试类Client.
1 using System; 2 3 namespace StrategyPattern 4 { 5 class Client 6 { 7 static void Main(string[] args) 8 { 9 FlyBehaviour flyBehaviour = new FlyWithWings(); //定义一个会飞的行为 10 FlyBehaviour notFlyBehaviour = new FlyNoWay(); //定义一个不会飞的行为 11 QuackBehaviour quackBehaviour = new QuackLoudly(); //定义一个嘎嘎叫的行为 12 QuackBehaviour notQuackBehaviour = new QuackMute(); //定义以一个不会叫的行为 13 14 Duck redDuck = new RedDuck("Mr.Red"); 15 redDuck.SetFlyBehaviuor(flyBehaviour); //动态添加飞行行为 16 redDuck.PerformFly(); 17 redDuck.SetQuackBehaviour(quackBehaviour); //动态添加嘎嘎叫行为 18 redDuck.PerformQuack(); 19 20 21 22 Duck rubberDuck = new RubberDuck("Mr.Rubber"); 23 rubberDuck.SetFlyBehaviuor(notFlyBehaviour); 24 rubberDuck.PerformFly(); 25 rubberDuck.SetQuackBehaviour(notQuackBehaviour); 26 rubberDuck.PerformQuack(); 27 28 Console.ReadKey(); 29 } 30 } 31 }
参考资料:《Head First 设计模式》