首页 > 技术文章 > 设计模式之策略模式

Tan-sir 2017-12-25 09:44 原文

 

定义

策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

设计原则

  • 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();
}

总结

现在我们在回到文章最开始来看看策略模式的定义:策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。所以我们可以把鸭子的每组行为当作是一个算法族,而里面每个具体的行为则是算法。

 

推荐阅读