zoukankan      html  css  js  c++  java
  • 「补课」进行时:设计模式(11)——游戏中的策略模式

    1. 前文汇总

    「补课」进行时:设计模式系列

    2. 游戏中的策略模式

    我是一个很喜欢玩游戏的人,周末在家打打游戏是真的很开心。

    回想起来当年上大学的往昔峥嵘岁月,那时候基本上是一个人在玩游戏,背后围着好几个人看,一个个的充当着狗头军师的作用。

    时间长了就能发现,喜欢看别人打游戏的人,往往自己玩的都不怎么样,但是当起狗头军师来那是一套一套的,难道这就是旁观者清?

    当年在大学宿舍玩的最多还是「英雄联盟」,当年还是 AP 剑圣横行天下,然而每次排位遇到的都是别人家的剑圣和我方剑圣。

    这时候,一般就是狗头军师上线的时候,你出这个 xxx ,保证你如何如何牛皮,哎呀,你先打谁谁谁啊,为啥老要追着一个肉砍。

    如果把上面这个场景转化成写程序,基本上是这样的:

    首先定义一个 LOL 的接口:

    public interface LOL {
        void playMethod();
    }
    

    然后再来两个狗头军师实现这个接口,每个狗头军师都有自己的玩法:

    public class DogStrategistA implements LOL{
        @Override
        public void playMethod() {
            System.out.println("先出攻击装,刚正面,不怂");
        }
    }
    
    public class DogStrategistB implements LOL {
        @Override
        public void playMethod() {
            System.out.println("先出防御装,站得住才有输出");
        }
    }
    

    接着,我们开启一局游戏:

    public class LOLGame {
        private LOL lol;
        public LOLGame(LOL lol) {
            this.lol = lol;
        }
        public void play() {
            this.lol.playMethod();
        }
    }
    

    然后下面是一个测试类:

    public class Test {
        public static void main(String[] args) {
            LOLGame game;
            System.out.println("狗头军师A的点子--------------");
            game = new LOLGame(new DogStrategistA());
            game.play();
            System.out.println("狗头军师B的点子--------------");
            game = new LOLGame(new DogStrategistB());
            game.play();
        }
    }
    

    最后的执行结果如下:

    狗头军师A的点子--------------
    先出攻击装,刚正面,不怂
    狗头军师B的点子--------------
    先出防御装,站得住才有输出
    

    是不是感觉上面这串代码好像和平时写的没啥区别,然而你并没有猜错,这就是策略模式。

    3. 策略模式

    3.1 定义

    策略模式(Strategy Pattern)是一种比较简单的模式,也叫做政策模式(PolicyPattern)。其定义如下:

    Define a family of algorithms,encapsulate each one,and make theminterchangeable.(定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。)

    • Context: 封装角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
    • Strategy: 抽象策略角色,策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。
    • ConcreteStrategy: 具体策略角色。

    通用代码如下:

    public interface Strategy {
        void doSomethinging();
    }
    
    public class ConcreteStrategy1 implements Strategy {
        @Override
        public void doSomethinging() {
            System.out.println("具体策略1");
        }
    }
    
    public class ConcreteStrategy2 implements Strategy {
        @Override
        public void doSomethinging() {
            System.out.println("具体策略2");
        }
    }
    
    public class Context {
        private Strategy strategy;
        public Context(Strategy strategy) {
            this.strategy = strategy;
        }
        public void doAnything() {
            this.strategy.doSomethinging();
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            Strategy strategy = new ConcreteStrategy1();
            Context context = new Context(strategy);
            context.doAnything();
        }
    }
    

    3.2 优点

    • 算法可以自由切换:这是策略模式本身定义的,只要实现抽象策略,它就成为策略家族的一个成员,通过封装角色对其进行封装。
    • 避免使用多重条件判断:如果没有策略模式,那么我们只能选择使用多重条件判断语句,多重条件语句不易维护,而且出错的概率大大增强。使用策略模式后,可以由其他模块决定采用何种策略,策略家族对外提供的访问接口就是封装类,简化了操作,同时避免了条件语句判断。
    • 扩展性良好:这甚至都不用说是它的优点,在现有的系统中增加一个策略太容易了,只要实现接口就可以了。

    3.3 缺点

    • 策略类数量增多:每一个策略都是一个类,复用的可能性很小,类数量增多。
    • 所有的策略类都需要对外暴露:上层模块必须知道有哪些策略,然后才能决定使用哪一个策略,这与迪米特法则是相违背的,我只是想使用了一个策略,我凭什么就要了解这个策略呢?那要你的封装类还有什么意义?这是原装策略模式的一个缺点,幸运的是,我们可以使用其他模式来修正这个缺陷,如工厂方法模式、代理模式或享元模式。

    如果系统中的一个策略家族的具体策略数量超过 4 个,则需要考虑使用混合模式,解决策略类膨胀和对外暴露的问题。

  • 相关阅读:
    JS-字符串截取方法slice、substring、substr的区别
    Vue中computed和watch的区别
    Vue响应式原理及总结
    JS实现深浅拷贝
    JS中new操作符源码实现
    点击页面出现爱心效果
    js判断对象是否为空对象的几种方法
    深入浅出js实现继承的7种方式
    es6-class
    详解 ESLint 规则,规范你的代码
  • 原文地址:https://www.cnblogs.com/babycomeon/p/13992552.html
Copyright © 2011-2022 走看看