一、认识策略模式
策略模式定义:定义了算法族,将其封装起来,让它们直接可以互相替换,此模式的话变化独立于算法的使用者。把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
结构:
抽象策略类:定义一个公共接口,各种不同的算法以不同过的形式来实现它,环境角色可使用这个接口来调用不同的算法。
具体策略类:实现了抽象策略类定义的接口,提供具体的算法实现。
环境类:持有一个策略类的引用,并最终给客户端使用。
优缺点:
优点:使用策略模式可避免多种条件语句;提供了一系列可重用的算法族;提供开闭原则,可在不修改代码情况下灵活增加新算法。
缺点:需要理解所有的策略算法区别,会造成很多的策略类,增加维护难度。
应用场景:
JDK中的Arrays.sort(T[] a, Comparator<? super T> c)方法,其中可以自定义使用Comparator接口来定义排序规则,这实际上也是一种策略模式体现。
Spring中的InstantiationStrategy接口包含了抽象实例化方法,用于创建实例,其实现类就实现了不同的创建方式有反射的、也有通过cglib方式的。
二、实现策略模式
2.1、 简单实现策略模式
demo见xyz.changlu.strategy.demo1包下代码:
抽象策略:Strategy
//抽象策略接口 public interface Strategy { void strategyMethod(); }
具体策略实现:ConcreteStrategyA、ConcreteStrategyB
//具体策略实现类A public class ConcreteStrategyA implements Strategy{ @Override public void strategyMethod() { System.out.println("具体策略类A实现算法...."); } } //具体策略实现类B public class ConcreteStrategyB implements Strategy { @Override public void strategyMethod() { System.out.println("具体策略实现类B执行算法...."); } }
指定环境类:Context
//指定环境类 public class Context { private Strategy strategy;//具体策略 public Context(){ //默认使用策略A this.strategy = new ConcreteStrategyA(); } public Strategy getStrategy() { return strategy; } public void setStrategy(Strategy strategy) { this.strategy = strategy; } //执行指定策略方法 public void strategyMethod(){ //通过调用指定的策略执行,可通过set/get方法更改策略 strategy.strategyMethod(); } }
该环境可随意切换指定策略!
测试程序:测试类Customer
//测试类 public class Customer { public static void main(String[] args) { Context context = new Context(); context.strategyMethod(); //更改指定策略之后再次使用 context.setStrategy(new ConcreteStrategyB()); context.strategyMethod(); } }
2.2、实际小案例实现策略模式
案例描述:根据需求需要创建多种不同类型的僵尸,并且其外观、移动、攻击会不停的迭代更新,问如何设计会更好!
本案例来源于b站视频:2020年最详细的23种Java设计模式完整视频教程全集
第一版:
第二版:
说明:我们可以看到第二版中僵尸类型变多,并且其外观、移动、攻击都会有所不同,若是不使用策略设计模式可能就会像如下方式设计。
abstract class Zombie{ abstract void describe(); abstract void appearance(); abstract void movie(); abstract void attach(); } //普通僵尸 class NormalZombie extends Zombie{ void describe(){//类型描述 .... } void appearance(){ .... } void movie(){ .... } void attach(){ .... } } //大头僵尸 class NormalZombie extends Zombie{ void describe(){//类型描述 .... } void appearance(){ .... } void movie(){ .... } void attach(){ .... } }
我们看上面的这种方式设计,使用一个抽象类包含其三个特征,接着不同类型僵尸来继承抽象类并实现指定方法。
弊端描述:若是我们要更改某个僵尸的部分特征呢?需要回到原本代码中去更改嘛,这就违反了开闭原则了,并且若是用户想要自定义岂不是行不通了。
实现过程
解决方案:分析一下每个僵尸都有不同的类型,其类型、外观、移动、攻击方式这个抽象名词是固有的,其中的行为会发生改变,我们这样修改,对于抽象类中的固有抽象方法并不改变,多添加两个需要不断改变的特定特征,如移动、攻击方式单独定义一个接口,不同的移动行为、攻击方式来实现该接口即可,并且在僵尸类中添加几个对应移动、攻击方式的实例属性,并且能够进行修改与获取,在调用相对应方法时调用其实例的方法即可!
demo见xyz.changlu.stratege.demo2包目录下代码:
Attachable:抽象攻击接口。 Moveable:抽象移动接口。 Zombie:为僵尸抽象类。 说明:箭头指向的表示对应的实现类,仅作演示所以只对攻击方式、移动方式做了接口及实现类,其他暂不使用接口,演示到位即可。 抽象行为:Attachable、Moveable //定义攻击接口 public interface Attachable { void attach(); } //定义行为接口 public interface Moveable { void movie(); }
抽象僵尸类:Zombie
//僵尸抽象类 public abstract class Zombie { Attachable attachable; Moveable moveable; //对应的抽象方法 abstract void describe(); abstract void appearance(); abstract void movie(); abstract void attach(); //初始化时具备指定能力 public Zombie(Attachable attachable, Moveable moveable) { this.attachable = attachable; this.moveable = moveable; } public Attachable getAttachable() { return attachable; } public void setAttachable(Attachable attachable) { this.attachable = attachable; } public Moveable getMoveable() { return moveable; } public void setMoveable(Moveable moveable) { this.moveable = moveable; } }
其中定义两个行为属性,并且包含set/get方法可进行切换设置行为属性。
定义了四个抽象方法分别为对应的不变的抽象行为。
具体行为:BiteAttach、HeadAttach、DirectionMove、StepToStepMove
//攻击行为实现类———————————————————————————————————————— //咬攻击 public class BiteAttach implements Attachable{ @Override public void attach() { System.out.println("咬攻击方式!"); } } //头部撞击攻击 public class HeadAttach implements Attachable{ @Override public void attach() { System.out.println("头撞攻击"); } } //移动行为实现类———————————————————————————————————————— //朝一个方向移动 public class DirectionMove implements Moveable { @Override public void movie() { System.out.println("朝着一个方法前进!"); } } //一瘸一拐移动 public class StepToStepMove implements Moveable{ @Override public void movie() { System.out.println("一撅一拐向前行进"); } }
具体僵尸类:NormalZombie、FlagZombie
//普通僵尸 public class NormalZombie extends Zombie { public NormalZombie() { //普通僵尸默认攻击为咬,移动方式为朝一个方向移动 super(new BiteAttach(), new DirectionMove()); } public NormalZombie(Attachable attachable, Moveable moveable) { super(attachable, moveable); } @Override void describe() { System.out.println("我是一个普通僵尸"); } @Override void appearance() { System.out.println("一顶绿帽子"); } @Override void movie() { moveable.movie();//指定指定行为接口方法 } @Override void attach() { attachable.attach(); } } //旗手僵尸 public class FlagZombie extends Zombie{ public FlagZombie() { //普通僵尸默认攻击为咬,移动方式为一瘸一拐移动 super(new BiteAttach(), new StepToStepMove()); } public FlagZombie(Attachable attachable, Moveable moveable) { super(attachable, moveable); } @Override void describe() { System.out.println("我是一个旗帜僵尸"); } @Override void appearance() { System.out.println("一顶绿帽子"); } @Override void movie() { moveable.movie(); } @Override void attach() { attachable.attach(); } }
测试程序:测试类Customer
//测试类 public class Customer { public static void main(String[] args) { //普通僵尸、旗帜僵尸的特征属性都来进行打印 System.out.println("--------默认情况--------"); NormalZombie normalZombie = new NormalZombie(); printFeature(normalZombie); FlagZombie flagZombie = new FlagZombie(); printFeature(flagZombie); //切换僵尸不同的特征,看僵尸的对应特征是否按照策略模式使用 //普通僵尸的攻击方式改为头撞方式,旗帜僵尸的攻击方式改为头撞方式,行走方式改为朝一个方向移动 System.out.println("--------修改指定的策略后--------"); normalZombie.setAttachable(new HeadAttach());//更改普通僵尸 flagZombie.setAttachable(new HeadAttach());//更改旗帜僵尸 flagZombie.setMoveable(new DirectionMove()); printFeature(normalZombie); printFeature(flagZombie); } //执行某个僵尸的所有动作 public static void printFeature(Zombie zombie){ zombie.describe(); zombie.appearance(); zombie.attach(); zombie.movie(); System.out.println(); } }
说明:通过策略模式我们可以自由的定义修改不同的行为,按照定义的策略执行方法!
总结
策略模式中定义了多个算法族,其定义一个接口包含抽象方法,不同的策略方式可以通过实现接口方法,让一个环境类持有对应该算法并且可进行随意改变策略使用!在游戏的角色装备中可以使用到对应策略模式,在Spring、JDK中也都有策略模式的应用。