策略模式(Strategy Pattern)
策略模式定义:策略模式定义了算法族,将算法分别封装起来,让他们之间可以互相替换。此模式让算法的变化独立于使用算法的客户
一个包含多种角色的小游戏,如何设计角色类?
设计一:
public abstract class Character { public abstract void act(); }
public class King extends Character { public void act() { System.out.println("Charge!"); } }
public class Knight extends Character { public void act() { System.out.println("Charge!"); } }
设计二,为了提高代码的复用性,通常的处理手段是将相同的行为移至角色类中:
public abstract class Character { public void act(){ System.out.println("Charge!"); }; }
public class King extends Character {}
public class Knight extends Character {}
游戏中肯定不会仅仅局限于“国王”和“骑士”这2个角色,现在需要一个新角色:“巫师”,“巫师”的能力是使用魔法
由于我们已经将“国王”与“骑士”的相同行为“冲锋”移至角色类中,对于新扩展的“巫师”,为了避免继承角色类的“冲锋”行为,需要重写方法以覆盖继承的“冲锋”行为
public class Wizard extends Character { public void act() { System.out.println("Magic!"); } }
在重构的过程中,会遇到以下几个问题:
1:每次添加新角色,都需要考虑父子类行为是否一致,并在需要时予以重写
2:新增加的类中再次出现相同的行为如何处理(“男巫”与“女巫”都使用魔法,但是“魔法”行为已经不能被移至父类中,可以看出设计二并没有从本质上解决复用性的问题)
3:如果某个行为的具体实现过程有变化,需找到所有具有该行为的具体角色类一一修改(从某种程度上来说是低复用性带来的问题)
可见这两种设计方式都不是最佳选择
通过“继承”、重写的方式实现角色的各种行为,会导致行为的具体实现和具体角色类相耦合,在此基础上扩展角色与角色行为必然会有很多问题,
以此也可以发现,在某些条件下,使用“继承”并不是最好的选择
从降低行为具体实现与具体角色类之间的耦合度着手,将具体行为进行封装,再通过组合的方式将行为动态赋予角色以实现行为具体实现与具体角色类之间的解耦
设计三,本质为:封装+组合:
public interface Act { public void act(); }
public class Charge implements Act { public void act() { System.out.println("Charge!"); } }
public class Magic implements Act { public void act() { System.out.println("Magic!"); } }
public class Character { protected Act act; protected void act(){ act.act(); } }
public class King extends Character { public King(){ this.act = new Charge(); } }
public class Wizard extends Character { public Wizard(){ this.act = new Magic(); } }
针对问题1:不需要考虑重写,只需考虑是使用现有行为类还是需要扩展Act接口实现
针对问题2:现在这已经不算是个问题了
针对问题3:修改Act接口实现类Magic,则所有拥有该行为的角色类都会自动更新
设计三即体现了策略模式的主要思想,“Act接口实现类”即为策略模式定义中的算法族,“Magic”类和“Charge”类即为策略模式定义中的算法
对上面的设计再进行一些修改,使我们能够动态改变角色的行为:
public class Character { protected Act act; protected void act(){ act.act(); } protected void setAct(Act act){ this.act = act; } }
public class Test { public static void main(String[] args) { Knight k = new Knight(); Wizard w = new Wizard(); k.act(); w.act(); k.setAct(new Magic()); k.act(); } }运行的结果:
Charge! Magic! Magic!