有很多同学都问我,老师是不是学了程序就能做魔兽了?呵呵,怎么回答呢,也是也不是吧。魔兽大家都玩过,3C的比赛一直关注度都是最高的,暴雪在商业运作上经常也是搞一些高额奖金的比赛来吸引观众的眼球。不过对于这个问题我也在想,要是做个魔兽的游戏该怎么做呢?大的方面不说,先来看看如果来设计农民呢。

首先我们会想到,农民应该有基本的几项功能:
Display ——显示外观
Excavate——采集挖掘
Move ——移动
Stop ——停止移动
这样我们可以设计出一个抽象类,只是Display方法需要也设置成abstract的,因为我们要设置的人族(Human)、兽族(Beast)、精灵族(Fairy)和不死族(Undeath)的农民外观是不一样的。需要分别实现。

图1

图1

突然想到一个问题,农民应该加入基本的进攻能力——Attack!很容易实现啊:我们在基类里加入了这个方法即可。可是,有个问题,这样Fairy也具有了进攻的能力,这样不行,因为事实上Fairy是不能具有进攻能力的!是啊,使用类的继承方式是有点问题了,一旦要对基类添加一些功能那么它的派生类也都具有了新的功能。这样很OK,简单的改进影响到全局;但是很不幸,不应该具有此功能的子类也不能幸免新功能的添加。

public abstract class Farmer
{

 public void Excavate(){ }

 public void Move(){ }

 public void Stop(){ }

 /// <summary>
 /// 显示
 /// </summary>
 /// <remarks>显示外观</remarks>
 abstract public void Display();

 public virtual void Attack()
 {
  Console.WriteLine(”Fire!Attack U!”);
 }
}
当然,我们可以在子类里可以覆盖定义此方法。
public class Fairy : Farmer
{
 public override void Display()
 {
  Console.WriteLine(”Fariy:…….”);
 }

 public override void Attack() {
  //啥也不写了
 }
}

说是如此,但是我们已经嗅到这里会有问题了:
1、也许以后我们为了游戏的发展还有其他的种族加入进来,那么可能那个种族的农民也不能有Attack方法,我们还要进行重写。
2、更重要的问题是,现在是我们加入了Attack这个方法,也许还会有许多其他的方法加入进来,而这个方法不能出现在所有的子类。
说穿了,使用继承并不能很好的解决问题,因为农民的行为在子类里不断的变化,并且让所有的子类都有这些行为是不恰当的。

找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。这个“封装变化”的思想几乎是每个设计模式背后的精神所在。

好了,我们这里需要变化的地方就是Attack,它不是跟随每个类都存在的。好了,独立它!并且我们要让它这一个攻击行为可以动态的改变。怎么做呢,我们需要的只是一个攻击的行为,至于到底如何攻击我们并不在意,所以我们把Attack提取出来,干脆定义成一个接口:
interface IAttackBehavior
{
 void Attack();
}
而基类里呢,我们肯定要用到这个接口的引用:
public abstract class Farmer
{
 internal IAttackBehavior attackbehavior;

 public void Attack()
 {
  attackbehavior.Attack();
 }
 
 ……
 
}

至于,到底要如何进行攻击呢,那只需要实现这个接口即可。比如,我们先来做两个行为实现:用武器进攻和不进攻。

图2

图2

/// <summary>
/// 用武器进攻
/// </summary>
class AttackWithWeapon : IAttackBehavior
{
 #region IAttackBehavior 成员

 public void Attack()
 {
  Console.WriteLine(”Hey, Fire! Attack!!!”);
 }

 #endregion
}
/// <summary>
/// 不进攻
/// </summary>
class NoAttack : IAttackBehavior
{
 #region IAttackBehavior 成员

 public void Attack()
 {
  Console.WriteLine(”I can NOT attack U! But i can burn your power!”);
 }

 #endregion
}
这样的思想既是针对接口编程,而不是针对实现编程。

好了,我们来修改一下精灵(Fairy)的进攻行为了,它是不能进攻的,所以实现不能进攻的行为类就好了:
public class Fairy : Farmer
{
 public Fairy()
 {
  base.attackbehavior = new NoAttack();
 }

 public override void Display()
 {
  Console.WriteLine(”Fariy:…….”);
 }
}
而人族的农民呢,则是用个榔头来进攻的:
public class Human : Farmer
{
 public Human()
 {
  base.attackbehavior = new AttackWithWeapon();
 }

 public override void Display()
 {
  Console.WriteLine(”Human:work work.”);
 }
}
同样的道理应用于兽族:
public class Beast : Farmer
{
 public Beast()
 {
  base.attackbehavior = new AttackWithWeapon();
 }

 public override void Display()
 {
  Console.WriteLine(”Beast:Ooo~~~Right”);
 }
}
到这里突然发现一个问题,不死族的农民是徒手进攻的啊!嗯,这里看出重构代码后应用了模式的好处,我们可以再来实现一个徒手进攻的行为就OK了:
class AttackWithoutWeapon : IAttackBehavior
{
 #region IAttackBehavior 成员

 public void Attack()
 {
  Console.WriteLine(”Haha, Beat!!!”);
 }

 #endregion
}
嗯,好了,可以让不死族的农民进攻了!
public class Undeath : Farmer
{
 public Undeath()
 {
  base.attackbehavior = new AttackWithoutWeapon();
 }

 public override void Display()
 {
  Console.WriteLine(”Undeath:some more.”);
 }
}

调用一下看看:
public static void Main()
{
 Console.WriteLine(”Hello, Design Patterns>>>>\r\n”);

 Human h = new Human();
 h.Display();
 h.Attack();

 Beast b = new Beast();
 b.Display();
 b.Attack();

 Fairy f = new Fairy();
 f.Display();
 f.Attack();

 Undeath u = new Undeath();
 u.Display();
 u.Attack();
 Console.Read();
}

最终的效果呢:
Hello, Design Patterns>>>>

Human:work work.
Hey, Fire! Attack!!!
Beast:Ooo~~~Right
Hey, Fire! Attack!!!
Fariy:…….
I can NOT attack U! But i can burn your power!
Undeath:some more.
Haha, Beat!!!

哈哈,真不错了。

图3

图3

如果我们要再添加一个种族呢?如果新种族的农民在进攻上有特殊的要求呢?做这些改变我们又要改动哪里呢?很困难么?
哇喔,重构了代码后,这些要求不会修改我们之前的所有代码,只是添加新的类和行为实现就好了!太酷了!!!

posted on 2009-10-03 02:30  雪美·考拉  阅读(297)  评论(0编辑  收藏  举报