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

同时在橡皮鸭和诱饵鸭的子类里面对其进行重写

 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("诱饵鸭不会飞");
        }
    }
View Code

 执行结果,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("这个鸭子看起来不会飞,所以啥都不做");
        }
    }
View Code

新增的叫的类组

   /// <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("这只鸭子它不会叫");
        }
    }
View Code

现在我们来使用这两个类组,先在超类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();

    }
View Code

 

现在我们对每个鸭子的子类进行整合,因为一起的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("橡皮鸭不会飞");
        }*/
    }
View Code

这样,我们在实例化每个鸭子的子类的时候,就将其行为已经设定完毕,我们在使用它的时候,完全不用关系其相关的子类的问题了。

执行结果

 

这里使用了另外一个原则--针对接口编程,不针对实现编程

 到这里我们的问题基本上都得到了解决。但是,需求的变化是永恒的,这个时候系统中加入了模型鸭,这种鸭子开始不会飞,但是当赋予这个鸭子一个飞行的动力后就可以飞行了,我们怎么解决这个问题呢。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("这个鸭子看起来不会飞,但是给它绑在火箭上它就飞了");
        }
    }
View Code
           Duck duckModel = new ModelDuck();
            duckModel.Display();
            duckModel.PerformQuack();
            duckModel.Swim();
            duckModel.SetFlyBehavior(new FlyRocketPowered());
            duckModel.PerformFly();
View Code

在调用的时候为鸭子的飞行行为赋值,OK,它飞起来了

 

这样我们就可以针对各种情况进行处理了

另外一个设计原则--多用组合,少用继承

 

总结:这个就是设计模式中的策略模式(Strategy Pattern)

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

 

感谢HeadFirst系列丛书的编辑团队,其实相对于这些干巴巴的知识,HeadFirst更难能可贵的是书本中的一些讨论,那些描述及场景,对于学习者来说有着醍醐灌顶的功效,有能力的人还是看看原著比较好。

代码在这里:http://pan.baidu.com/s/1hqEMzRY

 

posted @ 2015-04-30 17:44  西山农夫  阅读(250)  评论(0)    收藏  举报