设计模式之策略模式(实例+Demo)

马上要放清明小长假了,各位都想好去哪里玩了没?清明前的周日,整理下发型,梳理好心情,发个文章,再思考下清明去哪玩,哈哈哈。

《设计模式之策略模式(实例+Demo)》

不多废话,切入正题:

你从本文主要能了解到如下几点:1. 为什么要写这篇文章?2. 什么是策略模式?3.设计原则 4. 策略模式的实现及实例。5. 策略模式的优缺点。6.其他

1. 为什么要写这篇文章?

简单来说四个字:记录分享。用来记录供自己快速回顾,分享所学,供他人学习点评参考。

2. 什么是策略模式?

这个在文章末尾会给出解释,看耐心看完实例,相信你再看到定义会有更深刻的印象。

3. 设计原则:

设计原则有很多,这里直说策略模式中使用到的,参看实例思考哪些地方有用到下面的设计模式:
1. 封装变化(找出应用中可能需要变化之处,把它们独立出来,不要和哪些不需要变化的代码混在一起。)
2. 针对接口,超类编程,而不是针对实现编程。
3. 多用组合,少用继承。

4. 策略模式的实现及实例:

假设我们现在正在开发一款类似植物大战僵尸的游戏产品,产品经理告诉我们说:所有僵尸的必要条件是可以动,可以攻击,外观上可以区分就可以了。
这没问题,写一个抽象类Character,然后让所有角色(僵尸)继承这个类就可以了。So Easy.

抽象类

public abstract class Character {
    void move(){
        System.err.println("move");
    }
    void attack(){
        System.err.println("attack");
    }
    abstract void display();
}
public class RedHeadZombie extends Character { //红头僵尸
    @Override
    void display() {
        System.err.println("My head is Red");
    }
}

绿头僵尸:

public class GreenHeadZombie extends Character { 
    @Override
    void display() {
        System.err.println("My head is Green");
    }
}

写完了,问题解决,正准备看看新闻资讯的时候,产品经理走过来对我说,我们需要给我们每个僵尸设置一个前进速度,而且速度要有区别,不能所有僵尸都一样的。好吧,设置速度嘛,也简单,我在抽象类里面添加一个speed的方法,让红头僵尸和绿头僵尸都分别实现这个方法。也不难,分分种搞定。

public abstract class Character {
  //抽象类
    ……
    abstract void speed();
}
public class GreenHeadZombie extends Character { //绿头僵尸
    ……
    @Override
    void speed() {
        System.err.println("speed is 1.0");
    }
}
public class RedHeadZombie extends Character { //红头僵尸
    ……
    @Override
    void speed() {
        System.err.println("speed is 3.0");
    }
}

《设计模式之策略模式(实例+Demo)》

好吧,该有的速度也有了,提交代码,这下满足你了吧,产品经理这时走过来告诉你,僵尸的攻击方式不能一样的,你怎么让所有僵尸的攻击方式都一样了,这肯定不行的。好像有点道理的样子,那我就改咯。我重写所有的attack方法不就行了。

public class RedHeadZombie extends Character { //红头僵尸
    ……    
    @Override
    void attack() {
        System.err.println("RedHeadZombie's attack");
    }
}
public class GreenHeadZombie extends Character { //绿头僵尸
      ……
    @Override
    void attack() {
        System.err.println("GreenHeadZombie's attack");
    }
}

修改完成,攻击方式也不一样了。
《设计模式之策略模式(实例+Demo)》

这时候我偷偷瞄了一眼产品经理,发现这货正在看翘着二郎腿听歌呢!心里不服气,就多看了他一眼,然后眼神就对上了。这时他就缓慢放下他那秀气的腿,向我走过来……此处略掉一万字。反正最后结果就是,他觉得需要再添加两种僵尸(短腿僵尸,无攻击力僵尸),短腿僵尸速度很慢,但是可以远程攻击,无攻击力僵尸(不知道用来干嘛,可能是后勤部队,用来给其他僵尸运送军火的)。可以移动,但是不能攻击。还是乖乖写吧,继续写两个类ShortLegZombie(短腿),NoAttackZombie(无攻击)继承自Character.

public class ShortLegZombie extends Character { //短腿僵尸
    @Override
    void display() {
        System.err.println("I'm ShortLegZombie");
    }

    @Override
    void speed() {
        System.err.println("speed is slow");
    }

    @Override
    void attack() {
        System.err.println("ShortLegZombie's attack");
    }
}
public class NoAttackZombie extends Character { //无攻击力僵尸
    @Override
    void display() {
        System.err.println("I'm NoAttackZombie");
    }

    @Override
    void speed() {
        System.err.println("speed is 5.0");
    }

    @Override
    void attack() {
        //nothing to do
    }
}

完成了,还好,不算太慢。搞定。
《设计模式之策略模式(实例+Demo)》

过一会会,产品经理又走过来和我说,刚刚开会了,我们最后的决定是,你们开发再多添加下面几种僵尸来增加游戏的可玩性,丰富界面,提高用户可选性,辛苦你们了,最近加加班咯。好吧,一看,四十几种。
《设计模式之策略模式(实例+Demo)》

//好吧好吧,我写,我写可以吧?……四十几个僵尸类继承Character重写不同的diaplay(),speed(),attack()方法。(这个僵尸类百分百有其他变动,先写两个,等你下个需求我再看怎么修改咯!)

又添加了几个僵尸类,先拿给产品看一下。产品一看,眼睛微眯,皱着眉头:你这不行呀,你这僵尸怎么速度都是不变的?你遇到障碍物肯定的减速,没有障碍物得加速呀,这个要修改。僵尸在起跑线应该都不具有攻击方式,你这个攻击方式需要修改下。 听你这么一说好像有点道理,当僵尸满足某个条件时修改下速度状态和攻击方式。当是我现在已经创建十几个僵尸类了,这要修改的工作量有点大呀!而且还有几十种都没写上去呢!

能不能优化一下代码,让新建一个僵尸类的时候尽量用几行代码就搞定?而且可以随时修改其状态。

这样我们重写梳理下思路:我们编写一个抽象类(Character),让所有的僵尸都继承这个类。速度和攻击方式是可以动态改变的。我们将需要动态改变的部分抽取出来。我们分别编写ISpeedBehavior和IAttackBehavior两个接口,将所有的速度和攻击行为都抽取出来。速度和攻击这里暂时写三种:
《设计模式之策略模式(实例+Demo)》
接下里我们重新整理我们的代码:

抽象类Character及子类

public abstract class Character {
  //抽象类

    public IAttackBehavior mIAttackBehavior;
    public ISpeedBehavior mISpeedBehavior;

    void move(){
        System.err.println("move");
    }
    void attack(){
        mIAttackBehavior.attack();
    }
    void speed(){
        mISpeedBehavior.speed();
    }

    public void setIAttackBehavior(IAttackBehavior IAttackBehavior) {
        mIAttackBehavior = IAttackBehavior;
    }

    public void setISpeedBehavior(ISpeedBehavior ISpeedBehavior) {
        mISpeedBehavior = ISpeedBehavior;
    }

    abstract void display();
}
public class RedHeadZombie extends Character { //红头僵尸
    public RedHeadZombie(IAttackBehavior iAttackBehavior, ISpeedBehavior iSpeedBehavior) {
        mIAttackBehavior = iAttackBehavior;
        mISpeedBehavior = iSpeedBehavior;
    }
    @Override
    void display() {
        System.err.println("My head is Red");
    }
}

……

攻击行为类接口:IAttackBehavior及实现类

public interface IAttackBehavior {
    void attack();
}
public class OrdinaryAttack implements IAttackBehavior { 
    @Override
    public void attack() {
        System.err.println("I use ordinary attack");
    }
}

……

速度接口ISpeedBehavior及子类:

public interface ISpeedBehavior {
    void speed();
}
public class NormalSpeed implements ISpeedBehavior{ 
    @Override
    public void speed() {
        System.err.println("My speed is Normal!");
    }
}

……

最终动态修改速度和攻击的操作:

/** * 策略模式 */
public class StrategyActivity extends AppCompatActivity { 


    @BindView(R.id.zombie_tv)
    TextView mZombieTv;
    @BindView(R.id.speed_tv)
    TextView mSpeedTv;
    @BindView(R.id.attack_tv)
    TextView mAttackTv;

    private Character mCharacter;
    private IAttackBehavior mIAttackBehavior;
    private ISpeedBehavior mISpeedBehavior;
    private int mRandomZombieNum = 0;
    private int mRandomSpeedNum = 0;
    private int mRandomAttackNum = 0;
    private Random mRandom;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_strategy);
        ButterKnife.bind(this);
        //默认设置
        mIAttackBehavior = new OrdinaryAttack();
        mISpeedBehavior = new NormalSpeed();
        mCharacter = new RedHeadZombie(mIAttackBehavior,mISpeedBehavior);
        mRandom = new Random();
    }

    @OnClick({R.id.btn_change_zombie, R.id.btn_change_speed, R.id.btn_change_attack})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.btn_change_zombie:
                mRandomZombieNum = mRandom.nextInt(4);
                switch (mRandomZombieNum){
                    case 0:
                        mCharacter = new RedHeadZombie(mIAttackBehavior,mISpeedBehavior);
                        mZombieTv.setText("红头僵尸");
                        break;
                    case 1:
                        mCharacter = new GreenHeadZombie(mIAttackBehavior,mISpeedBehavior);
                        mZombieTv.setText("绿头僵尸");
                        break;
                    case 2:
                        mCharacter = new ShortLegZombie(mIAttackBehavior,mISpeedBehavior);
                        mZombieTv.setText("短腿僵尸");
                        break;
                    case 3:
                        mCharacter = new NoAttackZombie(mIAttackBehavior,mISpeedBehavior);
                        mZombieTv.setText("无攻击力僵尸");
                        break;
                }
                mCharacter.attack();
                mCharacter.speed();
                break;
            case R.id.btn_change_speed:
                mRandomSpeedNum = mRandom.nextInt(3);
                switch (mRandomSpeedNum){
                    case 0:
                        mISpeedBehavior = new NormalSpeed();
                        mSpeedTv.setText("My speed is normal");
                        break;
                    case 1:
                        mISpeedBehavior = new SlowSpeed();
                        mSpeedTv.setText("My speed is slow");
                        break;
                    case 2:
                        mISpeedBehavior = new FastSpeed();
                        mSpeedTv.setText("My speed is fast");
                        break;
                }
                mCharacter.setISpeedBehavior(mISpeedBehavior);
                mCharacter.speed();
                break;
            case R.id.btn_change_attack:
                mRandomAttackNum = mRandom.nextInt(3);
                switch (mRandomAttackNum){
                    case 0:
                        mIAttackBehavior = new OrdinaryAttack();
                        mAttackTv.setText("I use ordinary rtdinary attack");
                        break;
                    case 1:
                        mIAttackBehavior = new ReinforceAttack();
                        mAttackTv.setText("I use ordinary reinforce attack");
                        break;
                    case 2:
                        mIAttackBehavior = new SuperAttack();
                        mAttackTv.setText("I use ordinary super attack");
                        break;
                    case 3:
                        break;
                }
                mCharacter.setIAttackBehavior(mIAttackBehavior);
                mCharacter.attack();
                break;
        }
    }
}

这下ok了,随便你添加角色,随便你修改速度和攻击状态,我能随时修改。

《设计模式之策略模式(实例+Demo)》

效果图:
《设计模式之策略模式(实例+Demo)》

现在,我们来说下什么是策略模式?没错,我们上面的实例用的就是策略模式,分别将程序中变化的部分封装起来,让它们之间可以相互替换(速度和攻击行为)。也就是说策略模式可以让算法的变化独立于使用算法的客户。

5. 策略模式的优缺点

无论哪种模式都有其优缺点,当然我们每次在编写代码的时候需要考虑下其利弊。
策略模式的优点:
1. 提供了快速替换继承关系的办法:案例中的我们能快速的替换僵尸类,速度,攻击类型。
2. 减少代码中的if else 等判断语句:我们在更换攻击状态的时候不用去判断,当达到某个条件时直接切换攻击实现类就可以了。比如上文中的普通攻击变超级攻击。
3. 实现的选择:策略模式可以提供不同的实现。
策略模式的缺点:
1. 策略模式会造成很多策略类:上文中的所有速度实现类,攻击实现类……
2. 通讯开销很大:比如我们上面的速度接口的speed需要传递3个参数。这三个参数对有的僵尸类是有意义的,对大部分僵尸无意义,这时候就会造成没必要的通讯开销。
3. 必须自行知道所有的实现类有何不同?每次新建一个僵尸类的时候必须要知道所有的速度类及攻击实现类的不同。此时这些就会暴露出具体的实现问题。因此只有当行为类和角色行为相关的时候才需要使用策略模式。

6. 总结

现在我们回顾下刚开始说的策略模式所用到的三个设计原则:
1. 封装变化。我们将变化的速度和攻击行为封装出来,供动态改变调用。
2. 针对接口,超类编程,不针对实现编程。我们用ISpeedBehavior和IAttackBehavior接口去代表速度和攻击行为,每种具体的行为实现其中一个接口。
3. 多用组合,少用继承。组合最大的好处就是具有弹性,给代码维护和需求修改提供极大的便利。刚开始我们使用的就是继承的方式,在添加大量僵尸类的时候,代码的维护及修改量将非常大。后面优化过后我们就使用了速度和攻击类及僵尸类的组合去编写代码。

其实策略模式在游戏开发中是经常会使用到的。比如说街机西游机,有不同的角色,猪八戒,白龙马,孙悟空。他们都有武器,武器可能被妖怪打掉,也能自己捡起来。还能发不同的招数。当然这个就不编写代码了,重在思路。

最后,大家如果在学习的过程中有疑惑或者对本文的讲解觉得哪些地方值得改进,可以在评论下面留言。

《设计模式之策略模式(实例+Demo)》

打赏关注

If you find this repository helpful, you may make a donation to me via alipay or wechat.

坚持原创技术分享,您的支持将鼓励我继续创作!

微信打赏:支付宝打赏:知识星球:微信公众号:
《设计模式之策略模式(实例+Demo)》《设计模式之策略模式(实例+Demo)》《设计模式之策略模式(实例+Demo)》《设计模式之策略模式(实例+Demo)》
    原文作者:aserbao
    原文地址: https://blog.csdn.net/qq_32175491/article/details/79465496
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞