IBin 'S Space

勤学似春起之苗,不见其增,日有所长;辍学如磨刀之石,不见其损,日所有亏~
  博客园  :: 首页  :: 联系 :: 订阅 订阅  :: 管理

Head First设计模式笔记之(策略模式)

Posted on 2008-03-23 01:45  ibin  阅读(642)  评论(4)    收藏  举报

开篇

    最近开始学习设计模式,Head First这本书很奇怪,和一般地书籍不一样,一开始就一个策略模式。 不过,确实是本好书,但懂得设计模式,不代表你就是可以设计出优美的代码来,毕竟设计模式是实践中来,还是要回到实践中去的~。我打算把这个写成一个系列,作为自己学习的笔记,绝不"太监",也欢迎大家一起来讨论,我毕竟是个初学设计模式的鸟,从前还没设计模式这种概念,总感觉自己的代码很"脏",OK,废话太多了~!

策略模式定义

   The Strategy Pattern:a family of algorithms, encapsulates each one,and makes them interchangeable,Strategy lets the algorithm vary indepently from clients that use it.

材料,我就取自书本,重在理解。

让我们从鸭子开始吧~

    Joe是一个OO程序员,他上班的公司设计了一个产品—模拟鸭子的游戏,在游戏中,会产生很多的鸭子,有红头,绿头的,瘸腿的等,会一边游泳,一边呱呱叫的。一开始的设计是这样的:

可以派生出很多的不同种类的鸭子,因为每个鸭子都会游泳和呱呱叫,所以,直接由Duck这个抽象基类实现,但种类可能不同,display()方法是抽象的。"很好,很强大"~!但是,最近公司的竞争压力很大,其他公司推出了更好的产品了,那么怎么办?升级产品,扩展产品?OK,最后决定,在这个产品上增加会飞的鸭子,重新夺回市场。我们的Joe又开始工作了。。。

怎么办

    增加会飞的鸭子?好简单,我们在Duck类上再添加个fly()的方法,OK,好了~Joe,兴致冲冲地打电话给上司!2天后~,"Joe,你给我进来!"老上司直接吼到,完了,完了?怎么回事,原来是产品在演示的时候,突然出现了"会飞的橡皮鸭子",就是,所有的鸭子都会飞了,但是,橡皮鸭子会飞吗?那,在橡皮鸭子类中,我覆盖调继承下来的fly()方法,使它不在作用,不就可以了~?但是,如果以后再增加个不会叫也不会飞的鸭子呢,是不是也覆盖调继承下来的quck()的方法呢?那么,以后,我又增加些别的什么,是不是都这样做~?Joe,意识到了,有时候继承并不能很完美地解决问题,反而会越来越糟!继承Duck的方法会导致那些缺陷:

  • 代码在多个子类中重复
  • 运行时候,行为不容易改变(你不可能叫原来不会飞的鸭子,在运行时候会飞了~)
  • 很难知道鸭子的所有行为(就是,那么多的鸭子~)
  • 改变会牵一发而动全身

 

继承不行,那接口呢


    每次的更新,如果都要这样干,Joe想想都要崩溃了,接口行不行?Joe立刻想到,把fly()方法独立到一个类中,其他同类情况的也一样处理如何?最后设计成这样:

工作完成了??还没呢,大家想象一下,如果,我有几十个鸭子,那要一个一个这样添加,泪奔~!!!那有没有一种方法,既可以很少地更改原来的代码,又可以减少重构的实践?这个时候,就需要设计模式出马了。。。

策略模式

一个设计原则,要牢记:找出应用中可能需要变化之处,把它们独立出来,封装起来,区别那些不变化的部分。

这样,鸭子的行为就封装在一个专门的类中,专门提供某个行为的接口实现,但是,我们必须要使这个行为能有弹性,在运行的时候能动态地改变鸭子的行为,否则和前面设计的没什么区别了,如何做?下面,我们再牢记一个设计原则:针对接口编程,而不是针对实现编程。这里的针对接口编程的接口不是我们(主要使用C#)语言上的interface,而是一个概念,这个"接口"也可以是抽象类。下面这个图就很好地说明了:
这样设计,我们就不要具体是哪个鸭子实现,只要执行行为方法就行了!所以,在这里定义"接口",FlyBehavior和QuackBehavior,具体是怎么飞,怎么叫的,我们自己扩展到具体的类中,这样,以后要是在增加尖叫的,只要在实现个类(“啊啊”叫):

部分代码,代码没去测试过:
    public interface IflyBehavior
    
{
        
void fly();
    }

    
public interface IquackBehavior
    
{
        
void quack();
    }


    
//具体实现
    public class HengFly : IflyBehavior
    
{
        
public void fly()
        

            
//横着飞
        }

    }

    
public class ZhuanFly : IflyBehavior
    
{
        
public void fly()
        

            
//转圈地飞
        }

    }

    
public class GaGaQuack : IquackBehavior
    
{
        
public void quack()
        

            
//嘎嘎叫
        }

    }

    
public class ZhiZhiQuack : IquackBehavior
    
{
        
public void quack()
        

            
//吱吱叫
        }

    }
下面是鸭子基类:
    //鸭子基类
    public abstract class Duck
    

        
//定义两个字段分别来为表示行为接口,每个鸭子都会引用实现这俩个接口对象
        protected IflyBehavior flyBehavior;
        
protected IquackBehavior quackBehavior;

        
/*
            我们吧会飞和会叫的行为委托给这两个对象,     
         * 它们的fly()还是quack()都是由具体的实现去执行的,比如,我有个绿头鸭子是嘎嘎叫,
         * 那么这个DUCK对象是绿头鸭子,再调用行为是performQuack().它再去调用绿头鸭子的叫法
         * 大家仔细想象一下,领悟了吧~
         
*/

        
public void performFly()
        
{
            flyBehavior.fly();
        }

        
public void performQuack()
        
{
            quackBehavior.quack();
        }


        
//设置方法,只要调用方法了,就能动态创建对象
        public void SetflyBehavoir(IflyBehavior fly)
        
{
            
this.flyBehavior = fly;
        }

        
public void SetquackBehavior(IquackBehavior quack)
        
{
            
this.quackBehavior = quack;
        }

        
protected void swim()
        
{
            
//游泳的代码
        }

        
protected void display()
        

            
//显示外观
        }

    }
具体实现的一个类:
    public class oneDuck : Duck
    
{
        
public oneDuck()
        
{
            
//调用SetflyBehavoir和SetquackBehavior方法,初始化对象
        }

    }

结尾

策略模式:就是定义一系列的算法方法,并它们都封装起来,使它们都独立起来,并且使它们可以相互替换。该模式使得算法可独立于它们的客户变化。。主要是应对:在软件构建过程中,某些对象使用的算法可能多种多样,经常发生变化。如果在对象内部实现这些算法,将会使对象变得异常复杂,甚至会造成性能上的负担。