首页 > 代码库 > 浅谈设计模式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 ());
到现在我们的模拟发射子&弹程序就完成了,而我们在最后完成的版本就是利用的策略模式。
策略模式定义了一系列算法,并将每个算法封装起来,使他们之间可以互相替换。让算法独立于使用它的客户独立变化。
我们把发射子&弹定义成了一系列算法,达到相互替换,独立变化的效果。
组成:
—抽象策略角色:策略类,通常由一个接口或者抽象类实现。(子&弹接口)
—具体策略角色:包装了相关的算法和行为。(具体子&弹)
—环境角色:持有一个策略类的引用,最终给客户端调用。(People)
一般类图:
优缺点:
优点:
避免过多的条件语句,易于维护。
可以与模版方法模式组合使用,将实现类中共同代码转移到父类中。
实现类彼此完全独立,可以相互替换,便于扩展。
缺点:
策略过多时,会导致类数量庞大,不易维护。
本文出自 “huwei_lfc” 博客,请务必保留此出处http://8061813.blog.51cto.com/8051813/1535119