我们在开发时经常会遇到一堆的if else ..., 或者switch, 比如我们常见的全局异常处理等, 像类似这种很多if else 或者多场景模式下, 策略模式是非常受欢迎的一种设计模式, 然而, 一个好的策略模式却不是那么容易写出来.
我在工作中也因为写烦了switch,if else 觉得很不优雅, 因此,我考虑是否有一套统一的解决方案呢?
思考我问题的初衷, 在什么策略下, 满足什么条件执行什么动作, 返回什么值, 这就是策略模式需要解决的核心问题, 大眼一看好似有点类似状态机? 然而它并不是状态机, 状态机是比较笨重, 而策略机应该是足够轻量的.
我们再来看核心问题,关于什么策略,满足什么条件执行什么动作,返回什么值, 是一个明显的dsl语法, 因此, 我的基本语法糖已确立: strategy.of().when().perform()
或者strategy.of().perform()
, 因为有时我们并不需要条件, 仅仅策略即可.
我们要实现上述语法糖, 就得设计一套规则, 使其可以满足dsl, 并且是合理的, 如此, 基本定义已确定, 如下:
/**
* {@link StrategyMachineBuilder}
* @param <S> strategy
* @param <C> context
* @param <R> result
*/
public interface StrategyMachineBuilder<S,C,R> {
Of<S,C,R> of(S s);
StrategyMachine<S,C,R> build(String id);
}
/**
* {@link Of}
* @param <S> strategy
* @param <C> context
* @param <R> result
*/
public interface Of<S,C,R> {
When<S,C,R> when(Condition<S,C,R> condition);
StrategyMachineBuilder<S,C,R> perform(Action<C,R> action);
}
/**
* {@link When}
* @param <S> strategy
* @param <C> context
* @param <R> result
*/
public interface When<S,C,R> {
StrategyMachineBuilder<S,C,R> perform(Action<C,R> action);
}
/**
* {@link Condition}
* @param <S> strategy
* @param <C> context
* @param <R> result
*/
public interface Condition<S,C,R> {
boolean isSatisfied(S s,C c);
}
/**
* {@link Action}
* @param <C> context
* @param <R> result
*/
public interface Action<C,R> {
R apply(C c);
}
/**
* {@link Strategy}
* @param <S> strategy
* @param <C> context
* @param <R> result
*/
public interface Strategy<S, C, R> {
S strategy();
Condition<S,C,R> condition();
Action<C, R> action();
Strategy<S,C,R> strategy(S s);
Strategy<S,C,R> condition(Condition<S,C,R> condition);
Strategy<S,C,R> action(Action<C,R> action);
}
/**
* {@link StrategyMachine}
* @param <S> strategy
* @param <C> context
* @param <R> result
*/
public interface StrategyMachine<S,C,R> {
R apply(S s, C c);
}
如此: 架构已经构建完毕, 剩下的工作就很简单了, 实现此架构即可.
/**
* {@link StrategyMachineBuilderImpl}
* @param <S> strategy
* @param <C> context
* @param <R> result
*/
class StrategyMachineBuilderImpl<S,C,R> implements StrategyMachineBuilder<S,C,R>{
private final Map<S, List<Strategy<S,C,R>>> map = new ConcurrentHashMap<>();
@Override
public Of<S, C, R> of(S s) {
map.computeIfAbsent(s, k -> new ArrayList<>());
Strategy<S,C,R> strategy = new StrategyImpl();
map.get(s).add(strategy);
return new OfImpl(strategy);
}
@Override
public StrategyMachine<S, C, R> build(String id) {
StrategyMachineImpl<S, C, R> machine = new StrategyMachineImpl<>(map);
StrategyCache.put(id, machine);
return machine;
}
public class OfImpl implements Of<S,C,R>{
private final Strategy<S,C,R> strategy;
OfImpl(Strategy<S,C,R> strategy){
this.strategy = strategy;
}
@Override
public When<S, C, R> when(Condition<S,C,R> condition) {
this.strategy.condition(condition);
return new WhenImpl(strategy);
}
@Override
public StrategyMachineBuilder<S, C, R> perform(Action<C, R> action) {
this.strategy.action(action);
return StrategyMachineBuilderImpl.this;
}
}
public class WhenImpl implements When<S,C,R> {
private final Strategy<S,C,R> strategy;
WhenImpl(Strategy<S,C,R> strategy){
this.strategy = strategy;
}
@Override
public StrategyMachineBuilder<S, C, R> perform(Action<C, R> action) {
this.strategy.action(action);
return StrategyMachineBuilderImpl.this;
}
}
public class StrategyImpl implements Strategy<S, C, R> {
private S strategy;
private Condition<S,C,R> condition;
private Action<C, R> action;
@Override
public S strategy() {
return this.strategy;
}
@Override
public Condition<S,C,R> condition() {
return this.condition;
}
@Override
public Action<C, R> action() {
return this.action;
}
@Override
public Strategy<S, C, R> strategy(S s) {
this.strategy = s;
return this;
}
@Override
public Strategy<S, C, R> condition(Condition<S,C,R> condition) {
this.condition = condition;
return this;
}
@Override
public Strategy<S, C, R> action(Action<C, R> action) {
this.action = action;
return this;
}
}
}
/**
* Strategy Machine Impl
* @param <S> strategy
* @param <C> context
* @param <R> result
*/
class StrategyMachineImpl<S,C,R> implements StrategyMachine<S,C,R> {
private final Map<S, List<Strategy<S,C,R>>> map;
public StrategyMachineImpl(Map<S, List<Strategy<S,C,R>>> map){
this.map = map;
}
@Override
public R apply(S s, C c) {
List<Strategy<S, C, R>> strategies = map.get(s);
if (strategies==null||strategies.isEmpty()){
throw new RuntimeException("no strategy found for "+s);
}
for (Strategy<S, C, R> strategy : strategies) {
// 如果没有condition,直接执行action
if (strategy.condition()==null) {
return strategy.action().apply(c);
}
// 如果有condition,先判断是否满足condition,满足则执行action
if (strategy.condition().isSatisfied(s,c)){
return strategy.action().apply(c);
}
}
// 未发现策略关于s的condition
throw new RuntimeException("no strategy found of met condition for "+s);
}
}
/**
* Strategy Machine Factory
*/
public class StrategyMachineFactory {
public static <S,C,R> StrategyMachineBuilder<S,C,R> create() {
return new StrategyMachineBuilderImpl<>();
}
public static <S,C,R> StrategyMachine<S,C,R> get(String id) {
return (StrategyMachine<S, C, R>) StrategyCache.get(id);
}
}
/**
* {@link StrategyCache}
*/
class StrategyCache {
private static final Map<String,StrategyMachine<?,?,?>> CACHE = new java.util.concurrent.ConcurrentHashMap<>();
public static void put(String id, StrategyMachine<?,?,?> machine) {
CACHE.put(id, machine);
}
public static StrategyMachine<?,?,?> get(String id) {
return CACHE.get(id);
}
}
如此, 策略机已实现完毕. 下面给出两种场景例子
一. 不同年龄吃不同分量的药
Example:
Under the age of 12, take 20 milligrams of medication per day;
12-18 years old, taking 30 milligrams a day
18-30 years old, taking 40 milligrams a day
30-50 years old, taking 45 milligrams a day
Eating 42 milligrams for those over 50 years old
class MedicineStrategy {
private static StrategyMachine<String, MedicineContext, Void> strategy;
static {
StrategyMachineBuilder<String, MedicineContext, Void> machineBuilder = StrategyMachineFactory.create();
strategy = machineBuilder
.of("").when((s, c) -> c.age < 12).perform((c) -> {
System.out.println("Under the age of 12, take 20 milligrams of medication per day;");
return Void.TYPE.cast(null);
})
.of("").when((s, c) -> c.age >= 12 && c.age < 18).perform((c) -> {
System.out.println("12-18 years old, taking 30 milligrams a day");
return Void.TYPE.cast(null);
})
.of("").when((s, c) -> c.age >= 18 && c.age < 30).perform((c) -> {
System.out.println("18-30 years old, taking 40 milligrams a day");
return Void.TYPE.cast(null);
})
.of("").when((s, c) -> c.age >= 30 && c.age < 50).perform((c) -> {
System.out.println("30-50 years old, taking 45 milligrams a day");
return Void.TYPE.cast(null);
})
.of("").when((s, c) -> c.age >= 50).perform((c) -> {
System.out.println("Eating 42 milligrams for those over 50 years old");
return Void.TYPE.cast(null);
})
.build("medicine");
}
public static StrategyMachine<String, MedicineContext, Void> get() {
// StrategyMachine<String, MedicineContext, Void> strategy = StrategyMachineFactory.get("medicine");
return strategy;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class MedicineContext {
private int age;
}
public static void main(String[] args) {
get().apply("", new MedicineContext(10));
}
}
二. 计算机
StrategyMachineBuilder<String, StrategyContext, Number> machineBuilder = StrategyMachineFactory.create();
machineBuilder.of("加法").perform(strategyContext -> strategyContext.a + strategyContext.b);
machineBuilder.of("减法").perform(strategyContext -> strategyContext.a - strategyContext.b);
machineBuilder.of("乘法").perform(strategyContext -> strategyContext.a * strategyContext.b);
// 除法,当c==1时,忽略小数位, 当c==2时不忽略
machineBuilder.of("除法").when((s, strategyContext) -> strategyContext.c == 1).perform(strategyContext -> strategyContext.a / strategyContext.b);
machineBuilder.of("除法").when((s, strategyContext) -> strategyContext.c == 2).perform(strategyContext -> (strategyContext.a * 1.0d) / (strategyContext.b * 1.0d));
StrategyMachine<String, StrategyContext, Number> strategyMachine = machineBuilder.build("test");
// StrategyMachine<String, StrategyContext, Number> strategyMachine = StrategyMachineFactory.get("test");
System.out.println(strategyMachine.apply("加法", new StrategyContext(1, 2, 1)));
System.out.println(strategyMachine.apply("减法", new StrategyContext(1, 2, 1)));
System.out.println(strategyMachine.apply("乘法", new StrategyContext(1, 2, 1)));
System.out.println(strategyMachine.apply("除法", new StrategyContext(1, 2, 1)));
System.out.println(strategyMachine.apply("除法", new StrategyContext(1, 2, 2)));