策略模式
Strategy Pattern: Define a family of algorithms, encapsulateeach one, and make them interchangeable. Strategy lets thealgorithm vary independently from clients that use it.
定义一系列算法,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
把类(A)中的某一些方法,单独抽取为接口(B),变成 has-a 的关系,接口(B)的实现类具体完成某种算法/操作。B 接口的实现类称为策略类。
在A类中通过一个方法调用接口B中的方法,而具体的调用:由A的子类和接口B的实现类进行关联。
后续的更改接口B的实现类和A类的子类即可。便于动态改变、新增一些实现方法。
不明觉厉
看个实例:
某款游戏有一个角色(Character)类,具体角色包括国王(King)、皇后(Queen)、骑士(Knight)、妖怪(Troll),他们有赤手空打方法 fight(),使用武器攻打方法 useWeapon()。
做出类图 图1:
图1: 角色及其子类
现在完美的使用了类的继承机制,但仍然存在以下问题:
1 Queen 和 Troll 的代码存在重复,后面如果再有新角色,仍然使用匕首,同样需要重复书写。
2 如果再新增角色,一个小兵使用武器小刀,一个领帅使用长矛和匕首,新增两个类同时还有代码重复。如果新增 5 个,10 个角色,修改难度则变大。
代码如下:
// Character角色类
public class Character {
public void fight() {
System.out.println("赤手空拳就是干");
}
public void useWeapon() {}
}
//国王类
public class King extends Character {
public void useWeapon() {
System.out.println("国王还会使用宝剑");
}
}
//皇后类
public class Queen extends Character {
public void useWeapon() {
System.out.println("皇后还会使用匕首");
}
}
//骑士类
public class Knight extends Character {
public void useWeapon() {
System.out.println("骑士还会使用斧头");
}
}
//怪兽类
public class Troll extends Character {
public void useWeapon() {
System.out.println("妖怪还会使用匕首");
}
}
//主方法
public class Start {
public static void main(String[] args) {
Character king = new King();
king.fight();
king.useWeapon();
Character queen = new Queen();
queen.fight();
queen.useWeapon();
Character knight = new Knight();
knight.fight();
knight.useWeapon();
Character troll = new Troll();
troll.fight();
troll.useWeapon();
}
}
执行结果:
策略模式登场
既然这个行为(方法)具有如此多的变化性,不妨把它抽取出来。在角色类中我们只说执行使用武器的方法,具体的武器另实现。让具体的角色调用具体的武器。
类图变为图2:
图2:角色类/武器行为接口图
现在,新增角色,让其继承 Character 类即可;
新增武器,让武器实现WeaponBehavior接口即可。
至于使用的武器,在新增的角色类中来进行调用。
代码:
//Character角色类
import cn.headFirst.strategyMode.weapon.WeaponBehavior;
public class Character {
public WeaponBehavior weaponBehavior;
public void fight() {
System.out.println("赤手空拳就是干");
}
public void performUseWeapon() {
weaponBehavior.useWeapon();
}
}
//国王类
import cn.headFirst.strategyMode.weapon.SwordBehavior;
public class King extends Character {
public King() {
weaponBehavior = new SwordBehavior();
}
}
//皇后类
import cn.headFirst.strategyMode.weapon.KnifeBehavior;
public class Queen extends Character {
public Queen() {
weaponBehavior = new KnifeBehavior();
}
}
//骑士类
import cn.headFirst.strategyMode.weapon.AxeBehavior;
public class Knight extends Character {
public Knight() {
weaponBehavior = new AxeBehavior();
}
}
//怪兽类
import cn.headFirst.strategyMode.weapon.KnifeBehavior;
public class Troll extends Character {
public Troll() {
weaponBehavior = new KnifeBehavior();
}
}
//武器接口
package cn.headFirst.strategyMode.weapon;
public interface WeaponBehavior {
public void useWeapon();
}
//使用宝剑
package cn.headFirst.strategyMode.weapon;
public class SwordBehavior implements WeaponBehavior {
@Override
public void useWeapon() {
System.out.println("使用宝剑攻打");
}
}
//使用弓箭
package cn.headFirst.strategyMode.weapon;
public class BowAndArrowBehavior implements WeaponBehavior {
@Override
public void useWeapon() {
System.out.println("使用弓箭攻打");
}
}
//使用斧头
package cn.headFirst.strategyMode.weapon;
public class AxeBehavior implements WeaponBehavior {
@Override
public void useWeapon() {
System.out.println("使用斧头攻打");
}
}
//使用匕首
package cn.headFirst.strategyMode.weapon;
public class KnifeBehavior implements WeaponBehavior {
@Override
public void useWeapon() {
System.out.println("使用匕首攻打");
}
}
package cn.headFirst.strategyMode.character;
public class Start {
public static void main(String[] args) {
Character king = new King();
king.fight();
king.performUseWeapon();
Character queen = new Queen();
queen.fight();
queen.performUseWeapon();
Character knight = new Knight();
knight.fight();
knight.performUseWeapon();
Character troll = new Troll();
troll.fight();
troll.performUseWeapon();
}
}
执行结果:
问题星球再次抛出问题:
这样,我可以动态设置武器吗?
动态设置武器,就是执行过程中是可以让他切换武器,升级装备。
答案是肯定的,简单修改一下,在Character类中,给weaponBehavior加一个set方法,其余的保持不变,如图3。
图3:增加set方法后的Character类
代码如下:
//Character类中新增
public void setWeaponBehavior(WeaponBehavior weaponBehavior) {
this.weaponBehavior = weaponBehavior;
}
//主方法 执行中给国王新增一个武器 弓箭
king.fight();
king.performUseWeapon();
king.setWeaponBehavior(new BowAndArrowBehavior());
king.performUseWeapon();
结果:
在执行过程中,我们通过 setWeaponBehavior 方法就动态的为某一个对象新增武器。
由客户端决定在什么情况下使用哪种方法,客户端的压力比较大。
客户端:我是谁?我在哪?我要调用谁?
你要调用的就是策略,策略,策略。
原来策略模式用起来就是这样的啊,我的天,建了这么多类。
在上下文中找出行为策略,新建各种策略类,客户端去调用吧。
策略模式优点:
策略模式缺点:
感谢阅读,感谢陪伴!