设计模式之策略模式
问题:一个动作冒险游戏,有几个角色(如国王、皇后、骑士等),以及自身的武器使用。如何用代码进行实现。
方式一:定义个角色(character)基类,内中含有血量、怒气、外形显示,然后国王(king)与皇后(queen)类继承角色类。
当实际的角色类需要战斗技能时,只需要在基类(character)中添加虚的Fight()方法,那么即可在子类中根据需要进行重写。
如下面的展示图。
可是,如果以后我加入骑士、巨妖等角色,就要每一个都要执行Fight()的重写,且并不是每一个角色都是战斗类型的角色(如辅助类型角色)。此时利用继承来提供character类的行为,就会导致下列的4个缺点。
- 代码在多个子类中重复。
- 很难知道所有角色的全部行为。
- 运行时的行为不容易改变。
- 改变会牵一发动全身,造成其他角色不想要的改变。
方式二:利用接口如何。我可以把Fight()从超类中取出来,放进一个”IFightable接口”中,这么一来,只有战斗的角色才能实现此接口(如下所展示的结构图)。虽然IFightable接口可以解决“一部分”问题(不会战斗的角色),但却造成了代码无法复用,这只能是从一个恶梦跳进另一个恶梦。甚至,在会战斗的角色中,战斗的动作可能还有多种变化.....
如果能有一种建立软件的方法,好让我们可以用一种对既有代码影响最小的方式来修改软件该有多好啊。我们就可以花较少时间重做代码,而让程序去做更酷的事.....
幸运的是,在OO开发中有几个设计原则应该遵循(后续还有几个原则)。
设计原则:
- 封装变化,即找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
- 针对接口编程,而不是针对实现编程。(此中的接口应理解为超类型,不仅仅是Interface)。
- 多用组合,少用继承(“有一个”可能比“是一个”更好)。
方式三:针对上面的问题分析,策略模式正好适合此场景。(如下图)
策略模式:定义算法簇(把实现接口的一组行为理解为一簇算法),分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
/// <summary> /// 角色 /// </summary> public abstract class Character { public string Name { get; set; } public byte Level { get; set; } /// <summary> /// 血量 /// </summary> public ushort Blood { get; set; } /// <summary> /// 怒气 /// </summary> public ushort Anger { get; set; } /// <summary> /// 武器 /// </summary> private IWeaponBehavior weapon; public IWeaponBehavior SetWeapon { set { weapon = value; } } /// <summary> /// 战斗 /// </summary> public void Fight() { weapon.UseWeapon(); } public abstract void Display(); } public class King : Character { public King() { SetWeapon = new KnifeBehavior(); } public override void Display() { Console.WriteLine("我是国王"); } } /// <summary> /// 骑士 /// </summary> public class Knight : Character { public Knight() { SetWeapon = new SwordBehavior(); } public override void Display() { Console.WriteLine("我是骑士"); } } public class Queen : Character { public Queen() { SetWeapon = new BowAndArrowBehavior(); } public override void Display() { Console.WriteLine("我是女王"); } } /// <summary> /// 妖 /// </summary> public class Troll : Character { public Troll() { SetWeapon = new AxeBehavior(); } public override void Display() { Console.WriteLine("巨妖"); } } public interface IWeaponBehavior { void UseWeapon(); } /// <summary> /// 斧头 /// </summary> public class AxeBehavior : IWeaponBehavior { public void UseWeapon() { Console.WriteLine("实现用斧头砍劈"); } } /// <summary> /// 弓箭 /// </summary> public class BowAndArrowBehavior : IWeaponBehavior { public void UseWeapon() { Console.WriteLine("实现用弓箭射击"); } } /// <summary> /// 匕首 /// </summary> public class KnifeBehavior:IWeaponBehavior { public void UseWeapon() { Console.WriteLine("实现使用匕首刺杀"); } } /// <summary> /// 剑 /// </summary> public class SwordBehavior : IWeaponBehavior { public void UseWeapon() { Console.WriteLine("实现用宝剑挥舞"); } } [TestFixture] public class MyTest { [Test] public void Method() { King k = new King(); k.Fight(); k.SetWeapon = new BowAndArrowBehavior(); k.Fight(); } } //测试代码 [TestFixture] public class MyTest { [Test] public void Method() { Character character = new King(); character.Fight(); character.SetWeapon = new BowAndArrowBehavior(); character.Fight(); } }