小新大学毕业后进入一家游戏公司,最近公司准备研发一款叫皇室战争的卡牌对战游戏。玩家可以通过在战场上放置骷髅兵,王子,巨人,骑士,气球兵等各种各样的角色。这些角色相互拼杀最终摧毁敌方的国王塔赢得胜利。小新被分在了人物模型设计组跟着一位叫吉勇的资深程序员共同参与这项工作。小新从小就很喜欢玩游戏,拿到了任务后,非常积极,刷刷刷的就在黑板上画出了类图准备和他的师傅吉勇讨论。

吉勇老师请看图:所有的人物拥有不同的外表 (display)和攻击能力(attack)。角色的外表都各不相同,那么display()在Character中是一个抽象方法。Character的子类实现他们各自的display方法让不同的角色拥有不同的外表。而对于attack方法我们在Character中做一个默认实现。这样所有的角色就都能拥有攻击的能力。如果攻击的方式不同,那么我们只需要在子类中重写(Override)attack方法就好。

吉勇老师:看起来很有道理的样子,其实并不是这样。年轻人积极是好事,但是设计不能太过急躁,得反复斟酌。每一个角色都拥有攻击的能力这并不是一个好的设计。如果我们加入天使(Angel)这样的辅助类角色,她只能给友军回复生命,并不能攻击别人,你的设计就不灵了,一款好的游戏怎么能没有奶妈?

小新:看来是个问题,那我换一种方式。把攻击的能力定义为一个接口,如果想拥有攻击能力的只需要实现Attackable(攻击)接口。至于治疗也是一样,谁想拥有治疗技能,那么只需要实现Cureable(治疗)接口。

吉勇老师:恩,这是一个更加没有道理的设计。这让我们补了一个坑,但同时又挖了另外一个坑。接口的确让攻击和治疗以技能的形式呈现。但是人物的技能是通过实现接口的方式获得的。这样耦合度很高。你想想这样的情况,有些角色攻击方式是一样的,比如野蛮人(Barbarians)和骷髅兵(Skeletons)他们都用宝剑攻击敌人。按照你的设计野蛮人和骷髅兵需要各自实现一次Attackable接口,然后我们把用宝剑攻击的代码重复写两次。这样的设计导致野蛮人和骷髅兵用同样的方式攻击敌人但是代码却不能得到复用。我们重复的代码就会变得非常多。我们要设计一套代码,我传什么样的参数给他,他就用什么方式攻击或者辅助。今天野蛮人用剑,我一秒钟就让他改用长枪。这样的设计才能应对更多的变化。

小新:老师,我该如何去设计呢?

吉勇老师:首先,我给你第一点建议“找出应用中可能需要变化的地方,把他分离出来。不要和那些不变化的代码混在一起”。把那些会变化的部分封装起来,让其他部分不受影响。这样系统会更有弹性。

小新:分离“变化”与“不变”。野蛮人用剑,骷髅兵用剑,王子用长枪,天使不能攻击只能恢复友军生命。他们在战场上的行为是变化的。但是不变的是他们都会有行为。那么我需要把这些角色的行为抽象到一个行为接口(behavior)中去。用剑,还是用长枪这些具体的行为类实现这个behavior接口。

吉勇老师:很不错,这么快就领悟到了。那么在角色类(Character)中,调用的方法是具体用剑,用枪,还是治疗呢,你想想。

小新:都不是,应该调用Behavior接口。在写Character的时候,我哪里会知道今后具体会有多少种的行为呢,现在用剑和枪,以后你还可能用原子弹。但是我知道的是,你应该在战场上有一个行为Behavior,我只调用你的behavior,而我并不关心你具体的行为是什么。

吉勇老师:这是我想给你的第二个建议“面向接口编程,而不是面向实现编程”。这样使得你在写Character代码的时候并不需要关心Behavior的具体实现是什么。那么Behavior与Character是什么样的关系呢?你考虑过吗?

小新:刚才我是用Character实现Attackable接口获得attack技能。这样的方式并没有真正将角色和技能分开。角色中包含一个技能行为,而不是角色是一个技能行为。他们是组合关系。

吉勇老师:孺子可教也,我的第三个建议是“多用组合,少用继承”。让那些行为通过组合获得,而不是通过继承获得。再辛苦你画一个类图,并实现他吧。

代码如下:

public abstract class Character {

Behavior  behavior = null;

public void setBehavior(Behavior weaponBehavior) {

this.behavior = weaponBehavior;

}

public void behavior(){

String action = behavior.behavior();

System.out.println(display()+action);

}

abstract String display();

}

public class Barbarians extends Character {

@Override

String display() {

// TODO Auto-generated method stub

return "四个光膀子野蛮人";

}

}

public class Skeletons extends Character {

@Override

String display() {

return "四个小骷髅兵";

}

}

public class Prince  extends Character{

@Override

public String display() {

return "穿着盔甲的王子";

}

}

public class Angel extends Character{

@Override

String display() {

// TODO Auto-generated method stub

return "天使";

}

}

public interface Behavior {

public String behavior();

}

public class SwordBehavior implements Behavior {

@Override

public String behavior() {

// TODO Auto-generated method stub

return "使用大宝剑砍敌人";

}

}

public class LanceBehavior implements Behavior {

@Override

public String behavior() {

// TODO Auto-generated method stub

return "使用长枪戳敌人";

}

}

public class CureBehavior implements Behavior {

@Override

public String behavior() {

// TODO Auto-generated method stub

return "使用魔法治疗队友";

}

}

public class test {

public static void main(String[] args) {

// TODO Auto-generated method stub

Prince prince = new Prince();

Behavior behavior = new LanceBehavior();

prince.setBehavior(behavior);

prince.behavior();

Skeletons skeletons = new Skeletons();

behavior = new SwordBehavior();

skeletons.setBehavior(behavior);

skeletons.behavior();

Barbarians barbarians = new Barbarians();

behavior = new SwordBehavior();

barbarians.setBehavior(behavior);

barbarians.behavior();

Angel angel = new Angel();

behavior = new CureBeavior();

angel.setBehavior(behavior);

angel.behavior();

}

}

运行结果

穿着盔甲的王子使用长枪戳敌人

四个小骷髅兵使用大宝剑砍敌人

四个光膀子野蛮人使用大宝剑砍敌人

天使使用魔法治疗队友

吉勇老师:这样的设计就灵活多了。老板让我们再添加两个新角色,气球兵(Balloon)和炸弹兵(Bomber),他们一个在热气球里投掷炸弹攻击敌人,一个在地面投掷炸弹攻击敌人。

小新:全是小菜一碟。我们只需要添加三个类:Balloon, Bomber继承Character;BombBehavior实现Behavior接口就搞定。丢炸弹攻击敌人的行为还可以在气球兵(Balloon)和炸弹兵(Bomber)中得到复用。以后再来点什么炸弹王子也能复用BomerBehavior。在分开了人物和攻击方式后。如果哪天老板要让王子拥有投掷炸弹的能力,只需要在测试类Test中,用setBehavior()方法给王子设置一个BombBehavior。王子就不再是一个用长枪戳敌人的王子了,而是一个炸弹王子了。(Test类只是针对本程序的例子,而更好的做法不用修改任何代码,王子的行为写在配置文件中,另外添加一些读取配置文件的类,通过修改配置文件就能修改王子的修为,还改个屁的代码)

吉勇老师:完全正确,这就是松耦合的好处。我们通过定义算法族,再将他们封装起来,让算法族之间可以相互替换。这样使得算法的变化独立于调用他的客户代码。这么机智的设计就是策略模式。

彻底了解JAVA-策略模式(皇室战争是怎样炼成的)相关推荐

  1. java策略模式 if else_Java如何利用策略模式替代if/else语句

    平时在开发中避免不了使用大量的if else语句,但过多层的if else对于性能有很大的开销,类似如下代码 public class MainStart { public static void m ...

  2. Java两种设计模式_23种设计模式(11)java策略模式

    23种设计模式第四篇:java策略模式 定义:定义一组算法,将每个算法都封装起来,并且使他们之间可以互换. 类型:行为类模式 类图: 策略模式是对算法的封装,把一系列的算法分别封装到对应的类中,并且这 ...

  3. java策略模式实战

    项目:保险 场景:保险业务中涉及计费的问题,每种保险的计费策略不同,为了更好的代码维护,参考大神写的java策略模式后为选择了策略模式设计了编码方案. 涉及表: 产品方案表 业务简介: 每个保险产品对 ...

  4. java策略模式(Java策略模式多种返回结果)

    Java设计模式的中介者模式是怎样的? 如果对象之间的关系原本一目了然,中介对象的加入便是"画蛇添足". 来看下中介者模式的组成部分吧. 1) 抽象中介者(Mediator)角色: ...

  5. java策略模式案例_java策略模式典型案例

    java策略模式典型案例 java策略模式典型案例 [var1] 参考代码 : https://github.com/zhang-xiaoxiang/DesignPatterns23 没有用策略模式我 ...

  6. java策略模式_Java设计模式之策略模式详解

    本文实例为大家分享了Java策略模式,供大家参考,具体内容如下 1.策略模式(Strategy Pattern)是一种比较简单的模式,也叫做政策模式(PolicyPattern). 定义如下: Def ...

  7. java 策略模式会员_七:策略模式(不同等级会员打折算法)

    定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化: 下面给出策略模式的类图,引自百度百科. 策略模式在LZ第一次接触到的时 ...

  8. java策略模式 工厂模式_策略模式和工厂模式搭配使用

    策略模式和工厂模式的搭配使用可以很好地消除代码if-else的多层嵌套 需求 针对店下商铺,有这样一个需求,对用户客户分为了普通客户.vip客户.超级vip用户.专属vip用户4个等级,每当用户购买商 ...

  9. java策略模式详解_Java经典设计模式之策略模式原理与用法详解

    本文实例讲述了Java经典设计模式之策略模式.分享给大家供大家参考,具体如下: 策略模式指:策略模式指将程序中可变部分抽象分离成一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式 ...

最新文章

  1. JavaScript存在的原因
  2. java代码没错却运行不了_Java代码没错误,tomcat能正常运行,但是我的项目主页却一直显示不了,显示404错误...
  3. [PAT乙级]1038 统计同成绩学生
  4. .Net Core 3.0依赖注入替换 Autofac
  5. Linux centos7 安装 MySQL5.7.x
  6. Kafka:集群部署
  7. 不用 Python 自带的 Dict 实现自己的 HashTable
  8. 在Oracle中写出性能优良的SQL语句
  9. android shape 绘制气泡图,气泡图-自定义 shape
  10. python3.7版本简介_python3.7.2各平台安装简介
  11. 搜索系统中的纠错问题
  12. RabbitMQ消息持久化总结
  13. Ceph OSD简介
  14. 获取iOS设备唯一标识 uuid
  15. adb不是内部或外部命令,也不是可运行的程序
  16. 【苦练基本功2】求最小公倍数
  17. 哈工大计算机学院专业成绩公示,哈工大2009计算机学院录取名单及初试复试成绩排名...
  18. 投稿前,如何查询期刊投稿周期,4种亲测有效实用方法
  19. jsp+ssm计算机毕业设计超市收银系统【附源码】
  20. Python:一元二次方程求解

热门文章

  1. 讲课系列——评价政策模型
  2. 淘宝扣多少分会封店?规则是什么?
  3. excel宏字符串连接mysql_EXCEL 连接字符串 MYSQL
  4. SPI总线传输的模式
  5. tortoiseGit教程
  6. 合服 两个服务器都有什么作用,S2赛季转服功能介绍 第二赛季合服转服注意事项...
  7. 内部查询处理器错误:查询处理器无法生成查询计划
  8. iphone xr xs_如何在iPhone X,XS和XR上使用Animoji
  9. 了解数字隔离器安全使用的限制值
  10. 想学习Python? 知道这些开源电子书网站吗?