代码改变世界

策略者模式

2010-05-13 21:03  三皮开发时  阅读(565)  评论(2编辑  收藏  举报

1.起因

    Joe上班的公司做了一套相当成功的模拟鸭子游戏,游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫,系统的内部设计使用了标准的OO技术,设计了一个鸭子超类,并让各种鸭子继承此超类

  代码是这样写的

2.改进(扩展)

公司竞争压力加剧,在头脑风暴会议之后,主管认为该是创新的时候了,他们需要展示一些“真正”让人印象深刻的东西来振奋人心,主管确定需要会飞的鸭子来将竞争者抛在脑后,joe拍胸脯告诉主管,我只需要一个星期就可以搞定,毕竟joe是一个oo程序员嘛~

joe说:我只需要在Duck类中加上fly()方法,然后所有鸭子都会继承Fly()就噢了,展示OO的才华哇~

第二天,主管在股东会议上,看到了一下展示,有很多“橡皮鸭子”在屏幕上飞来飞去,橡皮鸭也会飞? 主管对joe说你是在开玩笑么?你可能要开始逛逛Monster.com(美国最大的求职网)了...

Joe他体会到了一件事:当涉及“维护”时,为了“复用”(reuse)目的而使用继承、结局并不完美。

Joe后来想到:我可以把橡皮鸭类中的fly()方法覆盖掉,就像Quack()的做法一样...Joe拿到主管的备忘录,希望以后每六个月更新产品(至于更新的方法,他们还没想到)。Joe知道规格会常常改变,每当有新的鸭子子类出现,他就要被迫检查并可能需要覆盖fly()和quack()...这简直是无穷无尽的噩梦。由此Joe想到可以把fly()和quack()从超类中取出来,各放进一个接口中

这真是一个超笨的主意,你没发现这么一来重复的代码会变多吗?如果你认为覆盖几个方法就算是差劲,那么对于48个Duck子类都要稍微修改以下飞行的行为,你又怎么说?并非所有的子类都具有飞行和呱呱叫的行为,所以继承并不是适当的解决方式,虽然用接口能接口不会再有会飞的橡皮鸭,但是却造成了代码无法复用,这只能算是从一个噩梦跳进了另一个噩梦,甚至,在会飞的鸭子中,飞行的动作可能还有多种变化....

这种情况要遵循以下几个设计原则:

  1.应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

  2.找出针对接口编程,而不是针对实现编程。

  3.多用组合,少用继承  

 

 3.如何解决

 

看明白了吧

IFly接口:

 public  interface IFly
    {
        
/// <summary>
        
/// 定义鸭子fly的方法
        
/// </summary>
        void Fly();        
    }

 

 

IQuack接口:

    public interface IQuack
    {
        
void Quack();
    }

 

 

行为类不会飞的鸭子继承fly接口

 class 不会飞的鸭子:IFly
    {        
        
public void Fly()
        {
            Console.WriteLine(
"I’m not fly!");
        }
    }

 

 

 

行为类会飞的鸭子继承fly接口

    class 会飞的鸭子:IFly
    {
        
public void Fly()
        {
            Console.WriteLine(
"i‘m flying!");
        }      
    }

 

 

 同理,行为类呱呱叫鸭子类继承IQuack接口

class 呱呱叫的鸭子:IQuack
    {
        
public void Quack()
        {
            Console.WriteLine(
"gua gua~");
        }
    }

 

 

 

行为类不会叫鸭子类继承IQuack接口

  class 不会叫的鸭子:IQuack
    {
        
public void Quack()
        {
            Console.WriteLine(
"I‘m not Quack!");
        }
    }

 

 

行为类吱吱叫鸭子类继承IQuack接口

 class 吱吱叫的鸭子:IQuack
    {
        
public void Quack()
        {
            Console.WriteLine(
"zhi~ zhi~");
        }
    }

 

 

Duck类!

 

代码
public class Duck
    {
       
public IFly fly;
        
public IQuack quack;

        
public void Swin()
        {
            Console.WriteLine(
"我会游泳");
        }

        
public void PerformQuack()
        {
            quack.Quack();
        }

        
public void PerformFly()
        {
            fly.Fly();
        }

        
public void SetFlyInterface(IFly fb)
        {
            fly 
= fb;
        }

        
public void SetIQuackInterface(IQuack qb)
        {
            quack 
= qb;
        }        
    }

 

 

现在,有个绿头鸭,它的特征是:会飞,也会呱呱叫,如何实现?

有个红头鸭,它的特征是:会飞,会吱吱叫,如何实现?

有个橡皮鸭,它的特征是:不会飞也不会叫,如何实现?

代码
 /// <summary>
    
/// 绿头鸭的特性是: 会飞,会呱呱叫
    
/// </summary>
    class MallardDuck:Duck
    {
        
//构造,这个很重要,在多态生成对象时,自动实现接口多态
        public MallardDuck()
        {
            fly 
= new 会飞的鸭子();//继承了Duck,所以能访问共有变量 fly
            quack 
= new 呱呱叫的鸭子();
        }        
    }

 

 

代码
  /// <summary>
    
/// 红头鸭的特征是:会飞,会吱吱叫
    
/// </summary>
    class RedHeadDuck:Duck
    {
        
//构造
        public RedHeadDuck()
        {
            fly 
= new 会飞的鸭子();
            quack 
= new 吱吱叫的鸭子();
        }
    }

 

 

代码
  /// <summary>
    
/// 橡皮鸭不会飞也不会叫
    
/// </summary>
    class RubberDuck:Duck
    {
        
//构造
        public RubberDuck()
        {
            fly 
= new 不会飞的鸭子();//继承了Duck,所以能访问共有变量 fly
            quack = new 不会叫的鸭子();
        }
    }

 

 

 

 

具体实现:

代码
      static void Main(string[] args)
        {
            Duck mallardDuck 
= new MallardDuck();
            Console.WriteLine(
"绿头鸭的特征:");
            mallardDuck.PerformFly();
            mallardDuck.PerformQuack();
            Console.WriteLine();

            Duck redDuck 
= new RedHeadDuck();
            Console.WriteLine(
"红头鸭的特征:");
            redDuck.PerformFly();
            redDuck.PerformQuack();
            Console.WriteLine();

            Duck rubberDuck 
= new RubberDuck();
            Console.WriteLine(
"橡皮鸭的特征:");
            rubberDuck.PerformFly();
            rubberDuck.PerformQuack();
            Console.WriteLine();

        }

 

 

这样就实现了要求,那现在,经过改造,橡皮鸭有“像火箭一样的飞行”的行为,如何实现?

 

写个行为类继承IFly,实现火箭飞行的方法

    class 火箭样的飞行:IFly
    {
        
public void Fly()
        {
            Console.WriteLine(
"像火箭样的飞行");
        }
    }

 

 

在Main函数中继续写入

 Console.WriteLine("橡皮鸭调换火箭后的特征:");
 rubberDuck.SetFlyInterface(
new 火箭样的飞行());//Duck rubberDuck = new RubberDuck();  更换接口,默认的接口行为是:不会叫,也不会飞,现,更改飞行接口
 rubberDuck.PerformFly();
 rubberDuck.PerformQuack();

 

 

Run!

 

 

4.结论

 

    策略者模式定义:定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。(这也就是为什么图3中为何把“行为”摸去改为算法的原因)

 

     策略者模式的优点:

          1、 提供了一种替代继承的方法,而且既保持了继承的优点(代码重用)还比继承更灵活(算法独立,可以任意扩展)。
          2、 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。
          3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。              
         
             策略者模式的缺点:          
           1、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。

 

ps:GRASP(General Responsibility Assignment Software Pattern)是通用职责软件分配模式。GRASP的核心是自己干自己能干的事,自己只干自己的 事,也就是职责的分配和实现高内聚。用来解决面向对象设计的一些问题。此外GRASP原则还阐述了几大原则,以后会继续写博道来。