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

继承很不错



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

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

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

于是设计了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接口就是兵器库(算法库),以后不管用到什么其他的战斗方式,只要在实现类里增加就行了,就像在兵器库(算法库)里增加兵器(算法),比如:双节棍,大刀,流星锤等等,如果有新的兵种直接拿来用就行了,实现了类的复用。

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

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

代码链接









在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章