HeadFirst设计模式(1)-策略模式(StrategyPattern)
开始学习HeadFirst 设计模式,学习笔记整理如下:
第一节策略模式(StrategyPattern)
HeadFirst给出的案例是模拟鸭子游戏的系统。按照一期系统设计,使用了Duck做为鸭子的超级类,实现鸭子共同行为呱呱叫(Quack)和游泳(Swim),同时实现了一个描述鸭子外观属性的抽象方法(Display)。每个鸭子的子类继承这个超级类,来实现各自的Disaply()。类图描述如下
代码如下:
鸭子的超级类:
/// <summary> /// 鸭子的超级类 /// </summary> public abstract class Duck { public void Quack() { Console.WriteLine("是鸭子都会呱呱叫"); } public void Swim() { Console.WriteLine("是鸭子也都会游泳"); } /// <summary> /// 对鸭子外观的描述 /// </summary> public abstract void Display(); }
鸭子的子类
public class MallardDuck:Duck { public override void Display() { Console.WriteLine("这个看起来是只绿头鸭"); } } public class RedheadDock:Duck { public override void Display() { Console.WriteLine("这个是只红头鸭"); } }
执行结果
这样一个简单的鸭子的系统就完成了。
现在,新需求来了,要求给鸭子加入新的行为:飞行。那么设计者认为,所有鸭子都是会飞行的,那么直接在Duck的超类里面添加Fly()方法即可。
OK这样修改貌似是没有问题,能够实现我们想要的。但是问题出现了,并不是所有的鸭子都是会飞行的,实际场景中添加了橡皮鸭和诱饵鸭,并且橡皮鸭不是呱呱叫,是吱吱叫;诱饵鸭是木头的不会飞也不会叫。
这时候我们想到了,将超类中的Quack和Fly进行覆盖
这样我们就需要将超类中的Quack和Fly方法定义为虚方法virtual
/// <summary> /// 鸭子的超级类 /// </summary> public abstract class Duck { public virtual void Quack() { Console.WriteLine("是鸭子都会呱呱叫"); } public void Swim() { Console.WriteLine("是鸭子也都会游泳"); } public virtual void Fly() { Console.WriteLine("所有鸭子都可以飞行"); } /// <summary> /// 对鸭子外观的描述 /// </summary> public abstract void Display(); }
同时在橡皮鸭和诱饵鸭的子类里面对其进行重写
public class RubberDuck : Duck { public override void Quack() { Console.WriteLine("橡皮鸭是会吱吱叫的"); } public override void Display() { Console.WriteLine("橡皮鸭"); } public override void Fly() { Console.WriteLine("橡皮鸭不会飞"); } } public class DecoyDuck:Duck { public override void Quack() { Console.WriteLine("诱饵鸭不会叫"); } public override void Display() { Console.WriteLine("诱饵鸭"); } public override void Fly() { Console.WriteLine("诱饵鸭不会飞"); } }
执行结果,OK完全达到了效果
貌似已经很完美了,但是一旦有新的鸭子类型进来,就需要检查Quack和Fly是不是需要重写,一旦数量非常大的时候,处理起来就将非常麻烦,因此HeadFirst给出了其他的解决方案,使用接口,将一些行为抽出来做成接口。但是一旦这些行为需要发生变化,就需要往下追踪到每一个定义此行为的类中进行修改,一不小心就造成错误,因此此方法也是有缺陷的。
这时候HeadFirst给出了一个设计原则--封装变化原则
描述:找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起。
根据这一原则,将Duck中的Quack和Fly方法抽出,简历一组新类来代表每个行为。按照这个原则,我们将用接口将每个行为进行封装,然后用具体的类对其进行实现。类图如下
这样的设计可以将这两个动作被其他对象复用,而且我们在新增一些行为的时候不会影响既有的行为类。
新增的飞的类组
/// <summary>
/// 飞的接口
/// </summary>
public interface IFlyBehavior
{
void Fly();
}
public class FlyWithWings:IFlyBehavior
{
public void Fly()
{
Console.WriteLine("这是只真鸭子,它会飞");
}
}
public class FlyNoWay:IFlyBehavior
{
public void Fly()
{
Console.WriteLine("这个鸭子看起来不会飞,所以啥都不做");
}
}
新增的叫的类组
/// <summary> /// 鸭子叫的接口 /// </summary> public interface IQuackBehavior { void Quack(); } public class Quack:IQuackBehavior { public void Quack() { Console.WriteLine("这只鸭子在呱呱叫"); } } public class Squeak:IQuackBehavior { public void Quack() { Console.WriteLine("这只鸭子在吱吱叫"); } } public class MuteQuack : IQuackBehavior { public void Quack() { Console.WriteLine("这只鸭子它不会叫"); } }
现在我们来使用这两个类组,先在超类Duck中进行修改,将这两个接口进行声明,并对飞行和呱呱叫两个行为进行重新编辑
我们来看修改后Duck的代码
/// <summary> /// 鸭子的超级类 /// </summary> public abstract class Duck { public IFlyBehavior flyBehavior; public IQuackBehavior quackBehavior; /*这个是优化前的代码 * public virtual void Quack() { Console.WriteLine("是鸭子都会呱呱叫"); } public virtual void Fly() { Console.WriteLine("所有鸭子都可以飞行"); }*/ public void PerformQuack() { quackBehavior.Quack(); } public void PerformFly() { flyBehavior.Fly(); } public void Swim() { Console.WriteLine("是鸭子也都会游泳"); } /// <summary> /// 对鸭子外观的描述 /// </summary> public abstract void Display(); }
现在我们对每个鸭子的子类进行整合,因为一起的Quack()和Fly()已经被抽出了,子类中已经不用再关心他们,可以将其删除。但需要处理的是对Duck中的变量flyBehavior和quackBehavior进行赋值。
public class MallardDuck : Duck { public MallardDuck() { quackBehavior = new Quack();//绿头鸭是呱呱叫 flyBehavior = new FlyWithWings();//绿头鸭会飞 } public override void Display() { Console.WriteLine("这个看起来是只绿头鸭"); } } public class RedheadDock:Duck { public RedheadDock() { quackBehavior = new Quack();//红头鸭是呱呱叫 flyBehavior = new FlyWithWings();//红头鸭会飞 } public override void Display() { Console.WriteLine("这个是只红头鸭"); } } public class DecoyDuck:Duck { public DecoyDuck() { quackBehavior = new MuteQuack();//诱饵鸭不会叫 flyBehavior = new FlyNoWay();//诱饵鸭不会飞 } public override void Display() { Console.WriteLine("诱饵鸭"); } /*优化前的代码 * public override void Quack() { Console.WriteLine("诱饵鸭不会叫"); } public override void Fly() { Console.WriteLine("诱饵鸭不会飞"); }*/ } public class RubberDuck : Duck { public RubberDuck() { quackBehavior = new Squeak();//橡皮鸭会吱吱叫 flyBehavior = new FlyNoWay();//橡皮鸭不会飞 } public override void Display() { Console.WriteLine("橡皮鸭"); } /*优化前的代码 * public override void Quack() { Console.WriteLine("橡皮鸭是会吱吱叫的"); } public override void Fly() { Console.WriteLine("橡皮鸭不会飞"); }*/ }
这样,我们在实例化每个鸭子的子类的时候,就将其行为已经设定完毕,我们在使用它的时候,完全不用关系其相关的子类的问题了。
执行结果
这里使用了另外一个原则--针对接口编程,不针对实现编程
到这里我们的问题基本上都得到了解决。但是,需求的变化是永恒的,这个时候系统中加入了模型鸭,这种鸭子开始不会飞,但是当赋予这个鸭子一个飞行的动力后就可以飞行了,我们怎么解决这个问题呢。HeadFirst给出了解决办法,继续重构Duck超类,在超类中加入两个方法,来设置这些行为。
新加入的模型鸭子类和它飞行的行为类
public class ModelDuck:Duck { public ModelDuck() { quackBehavior = new MuteQuack();//模型鸭不会叫 flyBehavior = new FlyNoWay();//模型鸭不会飞 } public override void Display() { Console.WriteLine("这是一只模型鸭"); } } public class FlyRocketPowered : IFlyBehavior { public void Fly() { Console.WriteLine("这个鸭子看起来不会飞,但是给它绑在火箭上它就飞了"); } }
Duck duckModel = new ModelDuck(); duckModel.Display(); duckModel.PerformQuack(); duckModel.Swim(); duckModel.SetFlyBehavior(new FlyRocketPowered()); duckModel.PerformFly();
在调用的时候为鸭子的飞行行为赋值,OK,它飞起来了
这样我们就可以针对各种情况进行处理了
另外一个设计原则--多用组合,少用继承
总结:这个就是设计模式中的策略模式(Strategy Pattern)
其定义为:定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
感谢HeadFirst系列丛书的编辑团队,其实相对于这些干巴巴的知识,HeadFirst更难能可贵的是书本中的一些讨论,那些描述及场景,对于学习者来说有着醍醐灌顶的功效,有能力的人还是看看原著比较好。
代码在这里:http://pan.baidu.com/s/1hqEMzRY

浙公网安备 33010602011771号