策略模式

模式定义

策略模式是一种软件设计模式,它可以在运行时动态的选择算法的行为。

例如:现在我们要完成一件事情,可以有多重策略,如策略A、策略B、策略C等。我们的需求是想要在不同的情况下选择使用不同的策略。在Java中,我们可以使用多态来实现这个功能。策略模式还体现了面向接口而不是面向实现来编程的原则。

示例说明

如下图所示,我们需要完成一个屠龙的任务。屠龙者(DragonSlayer)可以有三种策略来完成这个任务,分别为ProjectileStrategyMeleeStrategySpellStrategyDragonSlayer可以在运行时动态的选择这三种策略来完成屠龙的任务。
类之间的关系图如下所示:

策略模式的类的关系图
可以看出,这三种策略都实现了DragonSlayingStrategy这个接口,这个接口定义了一个execute的方法。

代码说明

1
2
3
4
5
6
/**
* 策略的接口
*/
public interface DragonSlayingStrategy {
void execute();
}

MeleeStrategy:

1
2
3
4
5
6
7
8
9
10
public class MeleeStrategy implements DragonSlayingStrategy {

private static final Logger LOGGER = LoggerFactory.getLogger(MeleeStrategy.class);

@Override
public void execute() {
//用你的神剑,你切断了龙的头!
LOGGER.info("With your Excalibur you sever the dragon's head!");
}
}

ProjectileStrategy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
*
* Projectile strategy.
*
*/
public class ProjectileStrategy implements DragonSlayingStrategy {

private static final Logger LOGGER = LoggerFactory.getLogger(ProjectileStrategy.class);

@Override
public void execute() {
//你用神奇的弩向龙射击,它倒在地上!
LOGGER.info("You shoot the dragon with the magical crossbow and it falls dead on the ground!");
}
}

SpellStrategy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
*
* Spell strategy.
*
*/
public class SpellStrategy implements DragonSlayingStrategy {

private static final Logger LOGGER = LoggerFactory.getLogger(SpellStrategy.class);

@Override
public void execute() {
//你施放了瓦解的魔咒,龙在一堆尘埃中蒸发了!
LOGGER.info("You cast the spell of disintegration and the dragon vaporizes in a pile of dust!");
}
}

下面来看看如何使用这三个策略,DragonSlayer 这个类通过组合的方式在自己的内部持有了一个策略。注意的是,该策略是一个接口,而没有具体指定是哪一个策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class DragonSlayer {
//持有了一个策略,并通过构造函数来初始化
private DragonSlayingStrategy strategy;

public DragonSlayer(DragonSlayingStrategy strategy) {
this.strategy = strategy;
}

//改变策略
public void changeStrategy(DragonSlayingStrategy strategy) {
this.strategy = strategy;
}
//战斗吧, 哈哈!
public void goToBattle() {
strategy.execute();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static void main(String[] args) {
// GoF Strategy pattern
LOGGER.info("Green dragon spotted ahead!");
DragonSlayer dragonSlayer = new DragonSlayer(new MeleeStrategy());
dragonSlayer.goToBattle();
LOGGER.info("Red dragon emerges.");
dragonSlayer.changeStrategy(new ProjectileStrategy());
dragonSlayer.goToBattle();
LOGGER.info("Black dragon lands before you.");
dragonSlayer.changeStrategy(new SpellStrategy());
dragonSlayer.goToBattle();

// 注意,在新的java8中,使用lambda表达式可以更方便的实现策略模式
//减少了代码量
LOGGER.info("Green dragon spotted ahead!");
dragonSlayer = new DragonSlayer(
() -> LOGGER.info("With your Excalibur you severe the dragon's head!"));
dragonSlayer.goToBattle();
LOGGER.info("Red dragon emerges.");
dragonSlayer.changeStrategy(() -> LOGGER.info(
"You shoot the dragon with the magical crossbow and it falls dead on the ground!"));
dragonSlayer.goToBattle();
LOGGER.info("Black dragon lands before you.");
dragonSlayer.changeStrategy(() -> LOGGER.info(
"You cast the spell of disintegration and the dragon vaporizes in a pile of dust!"));
dragonSlayer.goToBattle();
}
}

输出:

1
2
3
4
5
6
11:02:32.567 [main] INFO com.iluwatar.strategy.App - Green dragon spotted ahead!
11:02:32.616 [main] INFO com.iluwatar.strategy.App - With your Excalibur you severe the dragon's head!
11:02:32.616 [main] INFO com.iluwatar.strategy.App - Red dragon emerges.
11:02:32.616 [main] INFO com.iluwatar.strategy.App - You shoot the dragon with the magical crossbow and it falls dead on the ground!
11:02:32.616 [main] INFO com.iluwatar.strategy.App - Black dragon lands before you.
11:02:32.616 [main] INFO com.iluwatar.strategy.App - You cast the spell of disintegration and the dragon vaporizes in a pile of dust!