首页 > 代码库 > 浅谈设计模式1-策略模式

浅谈设计模式1-策略模式

对于大多数面向对象的初学者来说,将思维模式从面向过程转变过来是一个比较困难的过程。很多人在用面向对象语言编写程序的时候,依然会感觉自己在用面向过程的思维,笔者分享这篇文章的用意便是希望可以对大家有一些积极的影响。

阅读本文可以是没有接触设计模式,但需要一定的面向对象基础,至少简单理解封装,继承多态。

对于刚开始接触设计模式来说,一开始就说概念性的东西,很少能够理解。所以我们可以先跳过这些,通过一个小的程序场景来进行一个比较直观的认识。

模拟魂斗罗发射子&弹

相信大家小的时候玩过一款叫魂斗罗的游戏,玩家可以操作一个蓝色的小人,发射子&弹攻击敌人,并可以通过吃到高级弹药,来更换子&弹,增加威力。

首先,我们先用面向过程的方式实现一下,从逻辑上看我们首先需要一个可以发射子&弹的方法。

static void Fire(string bullet)

{

    if ("S".Equals(bullet))

    {

        Console.WriteLine("发射散弹");

    }

    else if ("C".Equals(bullet))

    {

        Console.WriteLine("发射普通子&弹");

    }

}

通过参数bullet来判断当前要发射什么样的子&弹。

接下来我们就可以在场景类中调用这个方法了。

static void Main(string[] args)

{

    //最开始使用普通子&弹

    string bullet = "C";

    Fire(bullet);

    //吃到散弹,可以发射散弹

    bullet = "S";

    Fire(bullet);

}

输出结果

        发射普通子&弹

        发射散弹

                  

这样我们发射子&弹的过程就简单的模拟好了,接下来我们用面向对象的思想来对这个过程做一下简单的封装。

发射子&弹的是我们操作的小人。所以我们可以先封装出一个People类,其中保存一个子&弹的变量,可以完成发射子&弹和更换子&弹的操作。

public class People

{

    //一开始使用的时普通子&弹。

    private string bullet = "C";

}

还要有一个发射子&弹的方法,当然在People类中。

public void Fire()

{

    if ("S".Equals(bullet))

    {

        Console.WriteLine("发射散弹");

    }

    else if ("C".Equals(bullet))

    {

        Console.WriteLine("发射普通子&弹");

     }

}

此外还可以更换子&弹

public void ChangeBullet(string bullet)

{

    this.bullet = bullet;

}

场景类更换为

static void Main(string[] args)

{

    People people = newPeople();

    people.Fire();

    people.ChangeBullet("S");

    people.Fire();

 

    Console.ReadKey();

}

输出结果不变。

写到这里我们会觉得是不是子&弹也是一个类,我们是不是需要把子&弹也封装一下。

首先他需要一个字段来记录当前是什么子&弹

public class Bullet

{

    private string status = "C";

     

    public Bullet()

    { }

 

    public Bullet(string status)

    {

        this.status = status;

     }

}

将小人中的开火方法搬移到子&弹类中。

public void Fire()

{

    if ("S".Equals(status))

    {

    Console.WriteLine("发射散弹");

    }

    else if ("C".Equals(status))

    {

    Console.WriteLine("发射普通子&弹");

     }

}

小人类变更为

public class People

{

    //一开始使用的时普通子&弹。

    private Bullet bullet = newBullet();

     

    public void Fire()

    {

         bullet.Fire();

    }

     

    public void ChangeBullet(string bullet)

    {

        this.bullet = new Bullet(bullet);

     }

}

运行结果相同。

写到这里我们的封装基本上做的差不多了,剩下的唯一看着不爽的,应该就是那一坨if/else语句了,我们都知道大量的if/else语句不利于阅读,难以维护,也不符合开闭原则,因为当我们需要添加新的子&弹类型的时候,我们需要更改子&弹的Fire()方法,更改if/else语句。所以我们是不是应该找一种方法来替换这些。这个时候就轮到多态登场了。

我们可以对将每一种子&弹都建立一个类,然后让他们继承同一个接口,这样我们在调用Fire()方法的时候,程序就会根据当前具体是哪一个对象,来调用该执行的发射子&弹方法了,在替换掉大量的if/else语句的同时,增加了程序的可读性和扩展性。

建立子&弹接口,其中包含Fire()方法。

public interface IBullet

{

    void Fire();

}

分别建立普通子&弹和散弹类,并都继承子&弹接口。实现Fire()方法。

//普通子&弹

public class CommonBullet : IBullet

{

    public void Fire()

    {

        Console.WriteLine("发射普通子&弹");

     }

}

 

//散弹

public class ScatterBullet : IBullet

{

    public void Fire()

    {

        Console.WriteLine("发射散弹");

    }

}

People类中更改为

public class People

{

    private IBullet bullet = new CommonBullet();

     

    public void ChangeBullet(IBullet bullet)

    {

        this.bullet = bullet;

    }

     

    public void Fire()

    {

        this.bullet.Fire();

     }

}

场景类中在调用更换子&弹时,改为

//吃到散弹,可以发射散弹

people.ChangeBullet(new ScatterBullet());

运行结果不变。

好了现在我们的程序中没有了if/else语句了,如果我们想要添加子&弹类型,我们只需要新建一个继承子&弹接口的实现类就可以了,而不需要再修改复杂的if/else语句。

//激光子&弹

public class LaserBullet : IBullet

{

    public void Fire()

    {

    Console.WriteLine("发射激光子&弹");

    }

}

 

调用的时候

//吃到散弹,可以发射散弹

        people.ChangeBullet(new LaserBullet ());

到现在我们的模拟发射子&弹程序就完成了,而我们在最后完成的版本就是利用的策略模式。

                            

wKiom1PeTGKTe9JuAAAdtRXaQDo546.png


策略模式定义了一系列算法,并将每个算法封装起来,使他们之间可以互相替换。让算法独立于使用它的客户独立变化。

我们把发射子&弹定义成了一系列算法,达到相互替换,独立变化的效果。

 

组成:

—抽象策略角色:策略类,通常由一个接口或者抽象类实现。(子&弹接口)

—具体策略角色:包装了相关的算法和行为。(具体子&弹)

—环境角色:持有一个策略类的引用,最终给客户端调用。(People

 

一般类图:

wKiom1PeTGKC0BTZAADogDxvTP0899.png

 

优缺点:

优点:

  1. 避免过多的条件语句,易于维护。

  2. 可以与模版方法模式组合使用,将实现类中共同代码转移到父类中。

  3. 实现类彼此完全独立,可以相互替换,便于扩展。

缺点:

  1. 策略过多时,会导致类数量庞大,不易维护。


本文出自 “huwei_lfc” 博客,请务必保留此出处http://8061813.blog.51cto.com/8051813/1535119