1.概念
很多情况下,实现某个目标的途径不止一条。比如我们要到一个地方去,可以选择交通方式(如:地 铁、公交、骑行、步行等等)有很多种
软件开发中,我们也常常会遇到类似的情况,实现某一个功能(如:排序、查找等)有多种算法,可如果用通过硬编码将多种算法集中在一个类中。后续如果新增一种算法必将修改算法类,也需要修改客户端代码来调用新的算法。在统一的算法类中封装 了大量的算法,代码非常复杂,不易维护和扩展。
此时我们可以使用一种设计模式来实现灵活地选择算法,还能方便的增加新的算法,策略模式(Strategy Pattern)应运而生。
定义
在计算机编程中,策略模式是一种行为软件设计模式,可以在运行时选择算法
2.策略模式的结构
包含如下几个角色:
- Context(环境类):环境类是使用算法的角色,它在解决某个问题(即实现某个方法)时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。
- Strategy(抽象策略类):它为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽 象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实 现的算法。
- Concrete Strategy(具体策略类):它实现了在抽象策略类中声明的算法,在运行时,具体策略 类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。
策略模式是一个比较容易理解和使用的设计模式,策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。
3.策略模式的实现的例子
3.1类图设计
3.2代码实现
业务相关的类
publicenumResultType{/***/NORMAL,WEAK_ENEMY,STRONG_ENEMY,CLEAR,MISTAKE,DEAD;}抽象策略类
定义了所有支持的算法(行为)的公共接口。在这里就是execute()方法,并规定统一返回ResultType(探索结果)
publicinterfaceIBehavior{ResultTypeexecute();}具体策略类
它们实现了IBehavior接口,各自封装了具体的逻辑和随机概率。
例如
NormalBehavior(正常搜索模式)遇到弱敌会返回WEAK_ENEMY;
AggressiveBehavior(攻击模式)遇到失误会返回MISTAKE等。
//攻击模式publicclassAggressiveBehaviorimplementsIBehavior{@OverridepublicResultTypeexecute(){if(newRandom().nextBoolean()){System.out.println("攻击模式:轻松解决敌人");returnResultType.CLEAR;}System.out.println("攻击模式:判断失误,对方很强,快防御");returnResultType.MISTAKE;}}//防御模式publicclassDefensiveBehaviorimplementsIBehavior{@OverridepublicResultTypeexecute(){if(newRandom().nextBoolean()){System.out.println("防御模式:暂时防守住了,赶紧跑吧");returnResultType.CLEAR;}System.out.println("防御模式:难道就这样结束了吗?我不甘心啊");returnResultType.DEAD;}}//正常模式publicclassNormalBehaviorimplementsIBehavior{@OverridepublicResultTypeexecute(){//随机产生探索结果Randomrandom=newRandom();intcount=random.nextInt(100);if(count<30){System.out.println("搜索模式:一切正常");returnResultType.NORMAL;}elseif(count<60){System.out.println("搜索模式:前方有人送装备");returnResultType.WEAK_ENEMY;}System.out.println("搜索模式:前方有强敌,建议逃跑");returnResultType.STRONG_ENEMY;}}环境类
内部维护了一个IBehavior类型的引用,并且提供了explore()方法。
使用了@Setter,这意味着可以在游戏运行的途中随时替换冒险者的行为(策略)。
publicclassAdventure{//维持了一个对抽象策略类的引用实例@SetterprivateIBehaviorbehavior;publicAdventure(IBehaviorbehavior){this.behavior=behavior;}publicResultTypeexplore(){returnthis.behavior.execute();}}客户端
publicclassClient{publicstaticvoidmain(String[]args){//从普通模式开始Adventureadventure=newAdventure(newNormalBehavior());mainLoop:do{//执行explore(),这里是策略模式的关键:不需要关心“当前是什么模式该怎么做”,它只需要无脑调用behavior.execute()即可//根据执行的结果去动态更新对象类型,选择下一步的操作switch(adventure.explore()){caseDEAD:System.out.println("System:Game Over");breakmainLoop;caseCLEAR:caseSTRONG_ENEMY:adventure.setBehavior(newNormalBehavior());break;caseMISTAKE:adventure.setBehavior(newDefensiveBehavior());break;caseWEAK_ENEMY:adventure.setBehavior(newAggressiveBehavior());break;caseNORMAL:default:break;}}while(true);}}演示
4 策略模式适用环境
策略模式用于算法的自由切换和扩展,它是应用较为广泛的设计模式之一。
策略模式对应于解决某一问题的一个算法族,允许用户从该算法族中任选一个算法来解决某一问题,同时可以方便地更换算法或者增加新的算法。
只要涉及到算法的封装、复用和切换都可以考虑使用策略模式。
4.1 主要优点
- 你可以在运行时切换对象内的算法。
- 你可以将算法的实现和使用算法的代码隔离开来。
- 你可以使用组合来代替继承。
- 你无需对上下文进行修改就能够引入新的策略,符合开闭原则。
- 提供了一种算法的复用机制,由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方 便地复用这些策略类。
- 可以避免多重条件选择语句。
4.2 主要缺点
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算 法的区别,以便适时选择恰当的算法。
- 策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系统要增加一个新的具体策略类。
- 无法同时在客户端使用多个策略类,也就是说,在使用策略模式时,客户端每次只能使用一个策略 类,不支持使用一个策略类完成部分功能后再使用另一个策略类来完成剩余功能的情况。
4.3 适用场景
- 一个系统需要动态地在几种算法中选择一种,那么可以将这些算法封装到一个个的具体算法类中, 然后通过里氏替换原则进行动态切换。
- 一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重条件选择语句来实现。
- 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法与相关的数据结构, 可以提高算法的保密性与安全性。
一句话总结
策略模式是一种行为设计模式,它将一系列不同的解决办法(算法)分别封装成独立的类,程序在运行时能够根据情况动态、自由地切换方法,方法之间可以相互替换,更换解决办法时无需修改原有的主流程代码。
如果这篇文章对你有帮助,欢迎点赞、评论、关注、收藏。你们的支持是我前进的动力!