ITPub博客

首页 > 数据库 > 数据库开发技术 > 策略模式玩转步兵,骑士和弓箭手

策略模式玩转步兵,骑士和弓箭手

原创 数据库开发技术 作者:zhanyd 时间:2020-05-11 08:30:00 0 删除 编辑


继承很不错

小帅刚毕业就进了一家游戏公司工作,公司正在开发一款新的即时战略游戏,领导让他设计游戏里的兵种及行为。

在第一个版本里,只有步兵,骑士和弓箭手三个兵种,每个兵种都有移动,停止,攻击和自愈四种行为。





小帅马上做了个简单的设计,每个兵种都能移动和停止,不同的兵种有不同的战斗方式和回血速度。

于是设计了Unit(兵种)抽象类,实现了move()和stop()方法,各个兵种继承该超类,然后根据自己的战斗特点实现超类的中的fight()和selfHealing()抽象方法。


子类重用的了父类的代码,又根据自己的特点实现了抽象方法,嗯,小帅觉得很满意,就在小组中做了分享。

“我觉得还应该加个投石车兵种,攻城利器”,小组成员提议。

“没问题,加个投石车的子类就行了”,小帅自信地说。


”可是投石车明明不能自愈,却还要实现父类的selfHealing()方法,不觉得很奇怪吗?”项目组的老王立马指出了问题。


接口怎么样?


小帅立马想到了接口,把自愈方法单独抽离出来,放到一个自愈接口里,有自愈能力的兵种都继承该接口。没自愈能力的投石车,就不用继承了。


”这样看上去好多了,但是代码不能复用,每个子类都要重新实现fight()和selfHealing()方法,以后我们要设计几十个兵种,每个兵种都得重新实现一遍,这样太麻烦了。”

老王犀利得指出了问题所在。

小帅一时语塞。


针对接口编程,而不是针对实现编程


老王看了看小帅一脸迷茫的样子,托了托眼镜,继续说道:“我们要找出应用中经常变化的部分,把它们独立出来,和那些不需要变动的代码区分开来,这是个重要的设计原则。”

“在这个例子中,fight()和selfHealing()方法每个子类的实现都不一样,属于经常变化的部分。

我们可以设计SelfHealingBeHavior和FightBeHavior接口,把战斗行为和自愈行为提取出来,这样就应用了一个设计原则:针对接口编程,而不是针对实现编程。”

“这里我们可以使用策略(Strategy)模式进行设计”,老王已经停不下来了,继续说道:

策略(Strategy)模式的定义如下:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。

这里的战斗行为和自愈行为就是“算法”,我们把算法封装起来,各种战斗和自愈的实现方式都在子类中实现,客户端只要调用接口就行了,不用管具体是怎么实现的。

应用了策略模式后的类图:


说了这么多,也该上代码了。


FightBeHavior接口以及实现类


/** * 战斗接口 * @author zhanyd */public interface FightBeHavior {    /**     * 战斗     */    void fight();}
/** * 剑战斗子类 * @author zhanyd */public class FightWithSword implements FightBeHavior{    public void fight() {        System.out.println("攻击:看我的剑法");    }}
/** * 弓箭战斗子类 * @author zhanyd */public class FightWithArrow implements FightBeHavior{    public void fight() {        System.out.println("攻击:我射箭-->");    }}
/** * 长枪战斗子类 * @author zhanyd */public class FightWithSpear implements FightBeHavior{    public void fight() {        System.out.println("攻击:看我的长枪");    }}
/** * 石头战斗子类 * @author zhanyd */public class FightWithStone implements FightBeHavior{    public void fight() {        System.out.println("攻击:我扔石头");    }}



SelfHealingBeHavior接口以及实现类



/** * 自愈接口 * @author zhanyd */public interface SelfHealingBeHavior {    /**     * 自愈     */    void selfHealing();}
/** * 快速回血子类 * @author zhanyd */public class SelfHealingFast implements SelfHealingBeHavior{    public void selfHealing() {        System.out.println("我每秒回血3%\n");    }}
/** * 中速回血子类 * @author zhanyd */public class SelfHealingNormal implements SelfHealingBeHavior{    public void selfHealing() {        System.out.println("我每秒回血2%\n");    }}
/** * 慢速回血子类 * @author zhanyd */public class SelfHealingSlow implements SelfHealingBeHavior{    public void selfHealing() {        System.out.println("我每秒回血1%\n");    }}




兵种类



/** * 兵种父类 * @author zhanyd */public class Unit {
protected String name;
protected FightBeHavior fightBeHavior;
protected SelfHealingBeHavior selfHealingBeHavior;
public void move() { System.out.println("我是:" + name); System.out.println("向前挺进..."); }
public void stop() { System.out.println("原地待命..."); }
/** * 动态设置战斗方式 * @param fi */ public void setFightBeHavior(FightBeHavior fi) { fightBeHavior = fi; }
/** * 动态设置自愈速度 * @param se */    public void setSelfHealingBeHavior(SelfHealingBeHavior se) { selfHealingBeHavior = se; }
/** * 战斗 */ public void fight() { fightBeHavior.fight(); }
/** * 自愈 */ public void selfHealing() { selfHealingBeHavior.selfHealing(); }}
/** * 步兵 * @author Administrator */public class Infantry extends Unit{    public Infantry() {        name = "步兵";        // 用剑攻击        fightBeHavior = new FightWithSword();        // 回血速度慢        selfHealingBeHavior = new SelfHealingSlow();    }}
/** * 弓箭手 * @author zhanyd */public class Archer extends Unit{    public Archer() {        name = "弓箭手";        // 用弓箭攻击        fightBeHavior = new FightWithArrow();        // 回血速度一般        selfHealingBeHavior = new SelfHealingNormal();    }}

/** * 骑士 * @author zhanyd */public class Knight extends Unit{    public Knight() {        name = "骑士";        // 用长枪攻击        fightBeHavior = new FightWithSpear();        // 回血速度快        selfHealingBeHavior = new SelfHealingFast();    }}
/** * 投石车 * @author zhanyd */public class Catapult extends Unit{    public Catapult() {        name = "投石车";        // 用石头攻击        fightBeHavior = new FightWithStone();        // 不能回血,不用设置回血功能    }}





测试类



public class TestUnit {    public static void main(String[] args) {        // 步兵出击        Unit unit = new Infantry();        unit.move();        unit.fight();        unit.stop();        unit.selfHealing();
// 弓箭手出击 unit = new Archer(); unit.move(); unit.fight(); unit.stop(); unit.selfHealing();
// 骑士出击 unit = new Knight(); unit.move(); unit.fight(); // 骑士扔掉了长枪,拔出了宝剑 System.out.println("动态设置:骑士扔掉了长枪,拔出了宝剑!"); unit.setFightBeHavior(new FightWithSword()); unit.fight(); unit.stop(); unit.selfHealing();
// 投石车出击 unit = new Catapult(); unit.move(); unit.fight(); unit.stop(); // 修理工给投石车做了保养,投石车也能回血了 System.out.println("动态设置:修理工给投石车做了保养,投石车也能回血了"); unit.setSelfHealingBeHavior(new SelfHealingSlow()); unit.selfHealing(); }}


测试结果


我是:步兵向前挺进...攻击:看我的剑法原地待命...我每秒回血1%
我是:弓箭手向前挺进...攻击:我射箭-->原地待命...我每秒回血2%
我是:骑士向前挺进...攻击:看我的长枪动态设置:骑士扔掉了长枪,拔出了宝剑!攻击:看我的剑法原地待命...我每秒回血3%
我是:投石车向前挺进...攻击:我扔石头原地待命...动态设置:修理工给投石车做了保养,投石车也能回血了我每秒回血1%


“setFightBeHavior(FightBeHavior fi)方法和setSelfHealingBeHavior(SelfHealingBeHavior se)方法,能在程序运行中动态的切换实现的算法,这是策略模式的一大优点。“,老王补充道。

小帅看着代码,陷入了沉思,真是越看越有味道。

“原来FightBeHavior接口就是兵器库(算法库),以后不管用到什么其他的战斗方式,只要在实现类里增加就行了。

就像在兵器库(算法库)里增加兵器(算法),比如:双节棍,大刀,流星锤等等,如果有新的兵种直接拿来用就行了,实现了类的复用。

战斗方式可以随时替换,并且不会影响到具体兵种的使用。“小帅貌似明白了策略模式的精髓所在,显得有点兴奋。

老王点点头,开心的笑了,“就是这样啊,你已经学会策略模式了”。


代码链接:https://github.com/zhanyd/learning-design-patterns











原文作者:编程我也会;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2018-06-20

  • 博文量
    120
  • 访问量
    51711