定义
策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计原则
- 1.找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
- 2.针对接口编程,而不是针对实现编程。
- 3.多用组合,少用继承:下面的例子中,将两个类结合起来,就是组合,将适当的行为对象组合起来使用。使用组合建立系统具有很大的弹性。
示例
现在有一些鸭子,它们有木头鸭、橡胶鸭、真鸭等,我们需要设计一段代码来表示它们。
第一次设计(使用OO)
在父类中加上fiy()就会导致所有的子类都具有此方法,但有的子类并不需要此方法。
缺点:需要覆盖不同实现的代码
第二次设计(使用接口)
所有的子类方法都从不同的接口实现,但会导致需要实现许多重复的代码。
缺点:代码无法复用
这里最好可以建立一种软件的方法,好让我们可以用一种对既有的代码影响最小的方式来修改软件。
第一次改进(封装变化)
根据设计原则一:
第二次改进(针对接口编程)
根据第二原则:
这里使用接口表示每个行为,而实际的现象不会被绑定在子类中,这样做可以让飞行动作被其他对象复用,因为这些行为已经与鸭子类无关了。
针对接口编程,关键在于多态。程序可以针对父类编程,在执行时会根据实际状况执行真正的行为,不会被绑死在父类的行为上。
针对父类编程:变量的声明类型应该时父类,通常可以是一个抽象类或者是一个接口,如此,只要实现此父类的类所产生的对象,都可以指定给这个变量,这也就意味着,声明类时不用理会以后执行时的真正对象类型!
做法:
针对实现编程:
声明变量为dog类型,针对具体实现编码
Dog d=new Dog(); d.bark();
针对接口/父类编程:
我们知道对象是dog,但是利用Animal 进行多态调用
Animal animal=new Dog(); animal.makeSound()
运行时才指定具体实现对象:
我们不知道它的子类型,我们只需要知道它能正确的进行makeSound就可以了
a=getAnimal();
a.makeSound();
实现鸭子的行为
实现父类Duck:
public abstract class Duck { public QuackBehavior quackbehavior;//引用实现QuackBehavior的接口对象 public FlyBehavior flyBehavior; public Duck() { } public abstract void display(); public void PerformFly() { flyBehavior.Fly();//委托给行为类 } public void PerformQuack() { quackbehavior.Quack();//委托给quackbehavior对象实现Quack } public void swim() { Console.WriteLine("All ducks float,even decoys!"); } }
飞行行为接口:
public interface FlyBehavior//所有飞行类都必须实现接口 { void Fly(); }
不能飞行行为对飞行接口实现:
public class FlyNoWay : FlyBehavior { public void Fly()//飞行实现,给不能飞的使用 { Console.WriteLine("I can‘t fly"); } }
能飞行行为对飞行接口实现:
public class FlyWithWings : FlyBehavior { public void Fly()//飞行实现,给可以飞的使用 { Console.WriteLine("I’m flying!!"); } }
叫声行为接口:
public interface QuackBehavior { void Quack(); }
三种叫声行为的接口实现:
public class Quack : QuackBehavior { void QuackBehavior.Quack() { Console.WriteLine("Quack"); } } public class MuteQuack : QuackBehavior { void QuackBehavior.Quack() { Console.WriteLine("MuteQuack"); } } public class Squeak : QuackBehavior { void QuackBehavior.Quack() { Console.WriteLine("Squeak"); } }
MallardDuck对象类实现:
class MallardDuck:Duck { public MallardDuck() { quackbehavior = new Quack();//当PerformQuack()被调用时,职责被委托给了Quack() flyBehavior = new FlyWithWings();//FlyWithWings作为FlyBehavior类型 } public override void display() { Console.WriteLine("I'm a real Mallard duck!"); } }
RubberDuck对象类实现:
public class RubberDuck : Duck { public RubberDuck() { quackbehavior = new MuteQuack(); flyBehavior = new FlyNoWay(); } public override void display() { Console.WriteLine("I'm a real Rubber duck!"); } }
测试:
static void Main(string[] args) { Duck mallard = new MallardDuck(); mallard.PerformQuack(); mallard.PerformFly(); Duck rubberDuck=new RubberDuck(); rubberDuck.PerformQuack(); rubberDuck.PerformFly(); Console.ReadKey(); }
动态设定行为
在Duck类中加入两个新方法:
public void setFlyBehavior(FlyBehavior fb) { flyBehavior = fb; } public void SetQuackBehavior(QuackBehavior qb) { quackbehavior = qb; }
新鸭子类型ModelDuck:
class MallardDuck:Duck { public MallardDuck() { quackbehavior = new Quack();//当PerformQuack()被调用时,职责被委托给了Quack() flyBehavior = new FlyWithWings();//FlyWithWings作为FlyBehavior类型 } public override void display() { Console.WriteLine("I'm a real Mallard duck!"); } }
建立一个新的飞行行为FlyBehavior:
public class FlyRocketPowered : FlyBehavior { public void Fly() { Console.WriteLine("I'm flying with a rocket!"); } }
测试:
static void Main(string[] args) { MoveSetBehavior.Duck modelDuck = new ModelDuck(); modelDuck.PerformFly(); modelDuck.setFlyBehavior(new FlyRocketPowered()); modelDuck.PerformFly(); Console.ReadKey(); }
总结
现在我们在回到文章最开始来看看策略模式的定义:策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。所以我们可以把鸭子的每组行为当作是一个算法族,而里面每个具体的行为则是算法。