王者荣耀之「策略者模式」

目录

  1. 什么策略模式(Strategy)
    1.1 Strategy模式的定义
    1.2 为什么用Strategy模式,他的优缺点
    1.3 传统策略模式的UML类图
    1.4 在android源码中使用的到的策略模式

  2. 通过王者荣耀的例子带大家熟悉一下 策略模式
    2.1 实现流程的第一代代码
    2.2 如何利用策略模式进行重构(共五步)

  3. 总结

前言

本文讲述了策略模式的基础以及应用的探索,为什么叫王者荣耀之「策略模式」呢,是因为在设计模式的基础上通过王者荣耀这款游戏实际举例,并且根据王者荣耀开始游戏的流程,通过策略模式进行优化改造。可以说扩展一下思维,在不同的业务上使用策略模式。

Java设计模式 王者荣耀之「建造者模式」是之前发布的一篇设计模式的文章。这回把它做成一个系列。

进入本文的主题:王者荣耀之「策略者模式」

1 首先我们了解一下什么策略模式(Strategy)

在软件开发中常常遇到这种情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。如果将这些算法或者策略抽象出来,提供一个统一的接口,不同的算法或者策略有不同的实现类,这样在程序客户端就可以通过注入不同的实现对象来实现算法或者策略的动态替换,这种模式的可扩展性和可维护性也就更高,这就是策略模式

1.1 Strategy模式的定义

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

1.2 为什么用Strategy模式,他的优缺点

简化逻辑和结构的同时,增强了系统的可读性、稳定性、可扩展性,对于较为复杂的业务逻辑显得更为直观,扩展也更为方便
优点

  • 结构清晰明了、使用简单直观
  • 耦合度相对而言较低,扩展方便
  • 操作封装更为彻底,数据更为安全

缺点

  • 随着策略的增加,子类也会变的繁多。

1.3 传统策略模式的UML类图


Strategy UML

角色介绍:

  1. Context 用来操作策略的上下文环境
  2. Strategy策略的抽象
  3. Concrete StrategyA Concrete StrategyB 具体策略的实现。

1.4 在android源码中使用的到的策略模式

在android源码中最常用到的策略模式模式就是使用动画中的差值器。插值器线性插值器、加速减速插值器、减速插值器等。在这里每种插值器内部都提供了具体的算法,但是使用的时候可以选择不同的插值器来使用。

附一些源码

public interface Interpolator extends TimeInterpolator {

}
public interface TimeInterpolator {
    float getInterpolation(float input);
}

abstract public class BaseInterpolator implements Interpolator {
    private @Config int mChangingConfiguration;
    /**
     * @hide
     */
    public @Config int getChangingConfiguration() {
        return mChangingConfiguration;
    }

    /**
     * @hide
     */
    void setChangingConfiguration(@Config int changingConfiguration) {
        mChangingConfiguration = changingConfiguration;
    }
}
//加速插值器
public class AccelerateInterpolator extends BaseInterpolator {
 //... 此处省略具体内容
}
//加速减速插值器
public class AccelerateDecelerateInterpolator extends BaseInterpolator{
 //... 此处省略具体内容
}

2 接下来通过王者荣耀的例子带大家熟悉一下 策略模式

说到王者荣耀玩过的都知道,在玩游戏之前有很多种玩法供选择。如果我们将从打开app到开始进行游戏的流程开发成一个程序的话流程大概是这样。打开app-->登陆-->选择对战模式(实战对抗、娱乐模式、人机对战、训练营、开房间)-->进入游戏。
为了逻辑清晰,供UML作为参考:


王者荣耀策略模式UML

2.1 实现流程的第一代代码

在这里,选择对战模式时有很多种模式,可能写出来的代码会是这样:

Ps:以下代码简单实现了以下对战选择模式的流程不需要细看,不必浪费时间和精力,他只告诉你是一个臃肿不利于维护的代码

public class StartGame {
    //不同的玩法
    public static final int 实战对抗 = 101; 
    public static final int 娱乐模式 = 102; 
    public static final int 排位模式 = 103; 
    //不同的地图
    public static final int 王者峡谷 = 201; 
    public static final int 深渊大乱斗 = 202; 
    public static final int 长平攻防战 = 203;
    public static final int 墨家机关道 = 204;
    public static final int 无限乱斗 = 205;
    public static final int 克隆大作战 = 206;
    public static void main(String[] args) {
        StartGame startGame=new StartGame();
        System.out.println("我要进入的对战模式是:"+startGame.selectBattle(实战对抗, 王者峡谷));
    }
    public String selectBattle(int way,int map) {
        if (way==实战对抗) {
            return arena(map);
        }else if (way==娱乐模式) {
            return recreation(map);
        }else if (way==排位模式) {
            return rank(map);
        }{
            return "待开发新模式";
        }
    }
    private String arena(int map) {
        if (map==王者峡谷) {
            return "匹配模式:3条对战线路,野区有野怪。10人对战5人一个组队,对战结束后增加经验和金币";
        }else if (map==深渊大乱斗) {
            return "大乱斗模式,1条线路,无野区。10人对战5人一个组队,对战结束后增加经验和金币";
        }else if (map==长平攻防战) {
            return "3v3模式,1条线路,野区有野怪。6人对战3人一个组队,对战结束后增加经验和金币";
        }else {
            return "其他";
        }
    }
    
    private String recreation(int map) {
        if (map==无限乱斗) {
            return "匹配模式:3条对战线路,野区有野怪。10人对战5人一个组队,对战结束后增加经验和金币";
        }else if (map==克隆大作战) {
            return "克隆大作战:3条对战线路,野区有野怪。每个队伍智能选择一个英雄,对战结束后增加经验和金币";
        }else  {
            return "暂未开发此功能";
        }
    }
    private String rank(int map) {
        if (map==王者峡谷) {
            return "匹配模式:3条对战线路,野区有野怪。10人对战5人一个组队,对战结束后增加经验和金币";
        }else  {
            return "暂未开发此排位模式";
        }   
    }
}

在这里,虽然现在的代码不多,逻辑稍有混乱,如果项目庞大了,出现了更多的内容当前的类就会更加臃肿。当前最明显的问题就是StartGame这个类不是单一原则,内部的功能太多。另一个是通过了if else来判断了,如果增加了其他的游戏模式,那是不是要写更多的if else呢,这样会越来越难以维护。

2.2 如何利用策略模式进行重构。

不用慌,策略模式So easy ! 此流程 五步走~

2.2.1 第一步 首先根据业务写出interface

根据各种游戏模式有不同的对战规则对战地图,这里需要2个接口

public interface IBattleMap {
     String map();
}

public interface IBattleRule {
    String rule();
}

2.2.2 第二步 实现具体的地图类和对战规则类

/**
 * 王者峡谷地图
 */
public class MapKingsCanyon implements IBattleMap {
    @Override
    public String map() {
        return "使用的王者峡谷地图,有野区,9座防御塔,1个主基地";
    }
}
/**
 * 墨家机关道地图
 */
public class MapMohistMechanical implements IBattleMap{
    @Override
    public String map() {
        // TODO Auto-generated method stub
        return "使用墨家机关道地图,无野区,1防御塔,1主基地";
    }
}

/**
 * 匹配模式
 */
public class RuleArena implements IBattleRule {

    @Override
    public String rule() {
        // TODO Auto-generated method stub
        return "10人对战5人一个组队,拆掉对方基地后获胜,对战结束后增加经验和金币";
    }
}
/**
 * 排位模式
 */
public class RuleRank implements IBattleRule {
    @Override
    public String rule() {
        // TODO Auto-generated method stub
        return "10人对战5人一个组队,拆掉对方基地后获胜,对战结束后增加经验和金币,以及排位星星";
    }
}
/**
 * 1v1 单挑模式
 */
public class RuleSolo implements IBattleRule {
    @Override
    public String rule() {
        // TODO Auto-generated method stub
        return "2人对战,拆掉对方基地后获胜,对战结束后增加少量经验和金币";
    }
}

2.2.3 第三步 BasePattern 具体的游戏模式基类

这里说明一下,这里增加的set方法是用来提高代码的扩展性。不然你要在每个具体的实现类的构造函数中创建具体的对战地图和对战规则对象了,现在你只需要在使用的地方直接set某个地图或者规则即可。如果我们现在的排位模式是王者峡谷地图,如果写死了,万一以后深渊大乱斗地图都出现排位了可怎么办...
而selectHero方法是由于每局游戏都是需要选择人物的,这里就认为他是一个通用的方法好了。

public abstract class BasePattern {
    IBattleMap mBattleMap;
    IBattleRule mBattleRule;
    public BasePattern() {
        
    }
    public void performMap() {
        if (mBattleMap != null) {
            System.out.println(mBattleMap.map());
        } else {
            System.err.println("请设置地图类型");
        }
    }
    public void performRule() {
        if (mBattleRule != null) {
            System.out.println(mBattleRule.rule());
        } else {
            System.err.println("请设置游戏类型");
        }

    }
    //公共方法
    public  abstract void selectHero();
    public void setmBattleMap(IBattleMap mBattleMap) {
        this.mBattleMap = mBattleMap;
    }
    public void setmBattleRule(IBattleRule mBattleRule) {
        this.mBattleRule = mBattleRule;
    }
}

2.2.4 第四步 列举5V5匹配模式和Solo模式的具体实现

/**
 * Solo模式具体实现
 */
public class SoloPattern extends BasePattern {
    @Override
    public void selectHero() {
        System.out.println("本局选择老兵不死,只会逐渐凋零的黄忠");
    }
}

/**
 * 普通匹配模式具体实现
 */
public class ArenaPattern  extends BasePattern{
    @Override
    public void selectHero() {
        System.out.println("本局选择将进酒,杯莫停的李白");
    }
}

2.2.5 最后 创建context角色类在这里实现他们

这里使用了Solo模式的玩法
/**
 * 优化后的StartGame类
 * 
 * @author PandaZwj
 * @date Mar 3, 2019
 */
public class OptimizeStartGame {
    public static void main(String[] args) {
        BasePattern mBasePattern=new ArenaPattern();
        mBasePattern.setmBattleMap(new MapMohistMechanical());
        mBasePattern.setmBattleRule(new RuleRank());
        mBasePattern.performMap();
        mBasePattern.performRule();
        mBasePattern.selectHero();
    }
}

打印结果如下:
使用墨家机关道地图,无野区,1防御塔,1主基地
2人对战,拆掉对方基地后获胜,对战结束后增加少量经验和金币
本局选择将进酒,杯莫停的李白


3 总结

好了这就是变种策略(Strategy)模式。通过上述的两个例子可以看出,前者通过if-else来解决问题。虽然实现起来简单,类行层级单一,但是代码臃肿,逻辑复杂,难以维护,没有结构可言。
使用了策略模式通过建立抽象,将不同的策略构建成一个具体的策略实现,通过不同的策略实现算法替换。在简化逻辑结构的同时,增强了系统的可读性、稳定性、可扩展性。更利于在更庞大的项目中。

策略模式主要用来分离算法,在相同的行为抽象下有不同的具体实现策略。这个模式很好的演示了开闭原则(对修改关闭对扩展开放,让程序更稳定更灵活),定义抽象,注入不同的实现,从而达到很好的扩展性。

好了策略模式就到这里了,本篇文章构思的时候查看了具体研究了一下现在王者荣耀游戏模式的各种入口,而且没忍住诱惑开了好几把。。一篇文章下来排位掉了好几颗星。。。如果喜欢本文的话,欢迎点击一下 “喜欢” 给予鼓励支持!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容