笑谈设计模式:策略模式

一、 惊!某程序员手持板砖走向策划,原因竟然是?!

阿毅是某公司的程序员,最近负责项目关于武将模块的开发,需求是这样子的:武将需要有名称,品质,可以进行升级,觉醒,不同武将升级,觉醒的逻辑可能相同也可能不同;目前需要实现刘备和关羽两个武将。

”不就是简单的面向对象吗?我一天就能搞定“,阿毅心里想。一天过后,代码长这样子:

public abstract class Hero {
    protected String name;
    protected int quality;

    protected abstract void levelUp();

    protected abstract void wake();
}
 
public class LiuBei extends Hero {

    public LiuBei(String name, int quality) {
        this.name = name;
        this.quality = quality;
    }

    @Override
    protected void levelUp() {
        //升级方案1
    }

    @Override
    protected void wake() {
        //觉醒方案1
    }
}
 
public class GuanYu extends Hero {

    public GuanYu(String name, int quality) {
        this.name = name;
        this.quality = quality;
    }

    @Override
    protected void levelUp() {
        //升级方案2
    }

    @Override
    protected void wake() {
        //觉醒方案2
    }
}

看上去没什么大问题,很好的利用的面向对象继承的特点。过了一阵子,策划和阿毅说:”上次的工作做得不错,但是为了丰富我们游戏的多样性,我们决定再增加两个武将:张飞(升级方案1,觉醒方案2)和诸葛亮(升级方案2,觉醒方案3)。“

虽然增加了需求,但是这对阿毅来说只是小菜一碟,不到一天,他就提交了代码:

public class ZhangFei extends Hero {

    public ZhangFei(String name, int quality) {
        this.name = name;
        this.quality = quality;
    }

    @Override
    protected void levelUp() {
        //升级方案1
    }

    @Override
    protected void wake() {
        //觉醒方案2
    }
}
 
public class ZhuGe extends Hero {

    public ZhuGe(String name, int quality) {
        this.name = name;
        this.quality = quality;
    }

    @Override
    protected void levelUp() {
        //升级方案2
    }

    @Override
    protected void wake() {
        //觉醒方案3
    }
}

虽说有部分代码冗余,但是能完成策划的需求,阿毅并没有在意,他并不知道,他正在给自己埋下一颗定时炸弹。

随着游戏运营越来越久,武将已经多达100个,重复代码也越来越多。此时,策划又提出一个需求:”我们要给武将新增一个进阶玩法,一共有三种进阶方案。时间比较赶,一天内完成。“

由于武将的进阶方案并不完全相同,因此我们无法通过在父类实现通用方法的方式来完成,这也就意味着,阿毅需要在100个武将类中,逐个添加进阶的方法:

public abstract class Hero {
    protected String name;
    protected int quality;

    protected abstract void levelUp();

    protected abstract void wake();

    protected abstract void advance();
}
 
public class LiuBei extends Hero {

    public LiuBei(String name, int quality) {
        this.name = name;
        this.quality = quality;
    }

    @Override
    protected void levelUp() {
        //初级升级
    }

    @Override
    protected void wake() {
        //初级觉醒
    }

    @Override
    protected void advance() {
        //进阶方案X
    }
}

//给其它99个类添加advance()方法

就在阿毅通宵敲代码,觉得大功即将告成之时,策划走过来和他说:”不好意思呀,我们刚刚开会,决定不要进阶这个功能了,改为天命升级,待会我把新需求发给你哈。“

此时的阿毅终于忍无可忍,操起手中的板砖。。。

二、原因分析

乍一看,上文似乎在讲一个勤勤恳恳的程序员被频繁加需求,改需求的策划逼得拿起板砖的故事,其实不然。

从程序出现大量冗余代码开始,阿毅就该意识到这一天迟早会降临。这种设计方式,哪怕仅仅改变某个升级方案的逻辑,也得同时修改几十个类。

也就是说,这种设计方式,使得代码极其难以维护!

来看看程序设计过程中,需要遵守的,阿毅却违反的几条规则:

  1. 将可能改变的代码抽取出来,与不会改变的代码分开;
  2. 面向接口编程,而不是面向实现;
  3. 有时候,比起继承,组合可能是个更好的选择。

接下来就来说说,策略模式是如何解决阿毅所遇到的问题的。

三、策略模式

Strategy Pattern defines a family of alogrithms, encapsulates each one, and makes them interchangable. Strategy lets the alogrithms vary from client that uses it.
策略模式定义了一个算法的集合,并对每一个算法进行封装,使它们能够被改变。策略模式也让客户端能够使用不同的算法。(翻译的不太好 o_o …)

光看定义有些难以理解,我们来看一下UML图。

  1. 阿毅的设计模式的UML:
    0. 策略模式 > stratyge_hero.png

    1. 策略设计模式UML:

0. 策略模式 > 111.png

将可能变化的方案的实现从Hero中抽离出来,通过面向接口编程,使得实现类的各种方案可变,易维护,减少大量重复代码。我们这里使用了组合的方式来实现,而不是继承。

具体的代码实现:

//升级策略
public interface LevelUpBehavior {
    void levelUp();
}

public class LevelUp1 implements LevelUpBehavior {
    @Override
    public void levelUp() {}
}

public class LevelUp2 implements LevelUpBehavior {
    @Override
    public void levelUp() {}
}

//觉醒策略
public interface WakeBehavior {
    void wake();
}

public class Wake1 implements WakeBehavior {
    @Override
    public void wake() {}
}

public class Wake2 implements WakeBehavior {
    @Override
    public void wake() {}
}

//武将共同接口
public abstract class Hero {
    protected String name;
    protected int quality;

	//不同武将有不同的策略
    protected LevelUpBehavior levelup; 
    protected WakeBehavior wake;

	//面向接口编程,不需要知道具体是哪一个策略
    public void levelUp() {
        this.levelup.levelUp();
    }

    public void wake() {
        this.wake.wake();
    }
}

//武将具体实现
public class GuanYu extends Hero {

	//构造函数中设置策略,也可以定义set()方法来改变策略
    public GuanYu(String name, int quality) {
        this.name = name;
        this.quality = quality;
        this.levelup = new LevelUp2();
        this.wake = new Wake2();
    }

}

public class LiuBei extends Hero {

    public LiuBei(String name, int quality) {
        this.name = name;
        this.quality = quality;
        this.levelup = new LevelUp1();
        this.wake = new Wake1();
    }

}
四、故事的结尾

原来,阿毅手中拿起的板砖并不是板砖,而是和板砖一样厚的《Head First Design Patterns》。看完里面讲解的策略模式后,阿毅重构了代码,最终将自己从堆积如山的重复代码中拯救出来。

”虽说加班让我掉了些许头发,但是学会了策略模式,我觉得还是挺赚的。“阿毅开心的提前下班了。

参考:Head First Design Pattern

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值