第二十三章:策略模式
一、情景引入
现在我们要做一个 rpg 游戏,rpg 游戏有多种职业,这些职业的行为会有所不同。请看下述方式实现各个职业有什么问题:
角色抽象类:
abstract public class Character {
public abstract void display();
public abstract void attack();
public abstract void cure();
public void flee() {
System.out.println("正在撤离");
}
}
具体角色类:
- 战士类
public class Warrior extends Character {
@Override
public void display() {
System.out.println("战士角色");
}
@Override
public void attack() {
System.out.println("使用物理攻击进行普攻");
}
@Override
public void cure() {
System.out.println("使用血瓶恢复 HP");
}
}
- 法师类
public class Witcher extends Character {
@Override
public void display() {
System.out.println("法师角色");
}
@Override
public void attack() {
System.out.println("使用魔法攻击普攻");
}
@Override
public void cure() {
System.out.println("使用治愈魔法恢复 HP");
}
}
根据职业不同,我们有不同的具体实现。不过,法师就不能喝药回复 HP?战士就不能使用魔法攻击?
为了让战士可以使用魔法攻击,我们是否需要再写个子类继承战士类,然后重写 attack 方法?
可以发现像 attack 和 cure 这种方法应该是可变的,通过继承实现是不合理的
二、模式简介
- StrategyA、StrategyB 策略接口
- ConcreteStrategy 根据不同的情形有不同的策略实现
- 通过聚合的方式动态的在 Subject 中更新策略
三、模式应用
攻击策略接口
public interface AttackStrategy {
void attack();
}
具体的攻击策略
public class MagicalAttack implements AttackStrategy{
@Override
public void attack() {
System.out.println("使用魔法攻击进行普攻");
}
}
public class PhysicalAttack implements AttackStrategy {
@Override
public void attack() {
System.out.println("使用物理攻击进行普攻");
}
}
治疗策略接口
public interface CureStrategy {
void cure();
}
具体的治疗策略
public class MagicalCure implements CureStrategy {
@Override
public void cure() {
System.out.println("使用治愈魔法恢复 HP");
}
}
public class MedcineCure implements CureStrategy{
@Override
public void cure() {
System.out.println("使用血瓶恢复 HP");
}
}
角色抽象类
abstract public class Character {
private AttackStrategy attackStrategy;
private CureStrategy cureStrategy;
public void setAttackStrategy(AttackStrategy attackStrategy) {
this.attackStrategy = attackStrategy;
}
public void setCureStrategy(CureStrategy cureStrategy) {
this.cureStrategy = cureStrategy;
}
public abstract void display();
public void attack() {
if (attackStrategy != null) {
attackStrategy.attack();
}
}
public void cure() {
if (cureStrategy != null) {
cureStrategy.cure();
}
}
public void flee() {
System.out.println("正在撤离");
}
}
战士类
public class Warrior extends Character {
@Override
public void display() {
System.out.println("战士角色");
}
}
法师类
public class Witcher extends Character {
@Override
public void display() {
System.out.println("法师角色");
}
}
具体策略对象有一份就行了,使用享元模式
public class StrategyFactory {
private Map<String, AttackStrategy> attackStrategies;
private Map<String, CureStrategy> cureStrategies;
public StrategyFactory() {
this.attackStrategies = new HashMap<>(2);
this.cureStrategies = new HashMap<>(2);
}
public AttackStrategy getAttackStrategy(String type) {
AttackStrategy strategy = attackStrategies.get(type);
if (strategy == null) {
switch (type) {
case "物理攻击":
strategy = new PhysicalAttack();
break;
case "魔法攻击":
strategy = new MagicalAttack();
break;
}
attackStrategies.put(type, strategy);
}
return strategy;
}
public CureStrategy getCureStrategy(String type) {
CureStrategy strategy = cureStrategies.get(type);
if (strategy == null) {
switch (type) {
case "药物治疗":
strategy = new MedcineCure();
break;
case "魔法治疗":
strategy = new MagicalCure();
break;
}
cureStrategies.put(type, strategy);
}
return strategy;
}
}
测试类
public class StrategyTest {
public static void main(String[] args) {
StrategyFactory strategyFactory = new StrategyFactory();
Witcher witcher = new Witcher();
witcher.setAttackStrategy(strategyFactory.getAttackStrategy("物理攻击"));
witcher.setCureStrategy(strategyFactory.getCureStrategy("魔法治疗"));
witcher.attack();
witcher.cure();
System.out.println("=== 改变策略 ===");
witcher.setAttackStrategy(strategyFactory.getAttackStrategy("魔法攻击"));
witcher.setCureStrategy(strategyFactory.getCureStrategy("药物治疗"));
witcher.attack();
witcher.cure();
}
}
/**********
使用物理攻击进行普攻
使用治愈魔法恢复 HP
=== 改变策略 ===
使用魔法攻击进行普攻
使用血瓶恢复 HP
**********/
四、模式总结
1)分析类中的可变部分和不变部分,可变部分做成策略簇
2)体现了多用组合聚合,少用继承实现
3)对象增加行为只需要添加一种策略,符合开闭原则
4)不过策略类可能会发生类膨胀