前言

公司早期开发的项目,接手后需要根据不同的data source 进行迭代优化。data source数据源非常多,不同的数据源需要根据不用的产品规则进行处理。因此,老代码的里大量的if else判断非常多······。说实话刚开始不太敢动手改,非常头疼。但是经过一番思考过后,毅然决定进行重构。所谓长痛不如短痛······

而消除if else 有很多方法,网上目前很多类似的例子。大多都比较抽象,实际的应用过程中,就需要自己独立思考,实现适合当前项目的最佳方式。笔者一开始也遇到了不少麻烦。下面本文通过结合Flink中常用的map算子,实现策略模式+工厂模式的应用。

定义

首先说明下该设计模式的定义,策略模式是指有一组算法,它将每个算法都封装起来,并且可使它们之间可以相互替换,在客户端调用它们时可以互不影响。

这里需要理解三个角色

  1. 抽象策略
  2. 具体策略
  3. 上下文环境
  • 抽象策略是接口或者抽象类的封装,一般来定义一个方法,可以写成函数式接口,满足单一职责的原则。
  • 具体策略是用于实现抽象策略的子类或实现类。是消除复杂if判断的关键,不同的策略实现类承接不同的业务方法和逻辑。
  • 上下文环境可以理解为对抽象策略的使用,客户端调用上下文环境

UML

solutionpng

上面的UML类图举了一个导航的例子,到达目的地有多种策略,公共交通、自驾、步行都是抽象策略的具体实例。而我们手机里的导航,就是上下文环境。客户端就是用户,用户通过导航来实现策略的使用。

此处省略示例代码

实战应用

下面是项目实际中使用的代码。去除了业务相关的属性,减少了策略实现类的数量,旨在说明问题。

首先定义抽象策略接口:

1
2
3
public interface IBaseStrategy {
List<OutputEntity> doCleanFieldAlg(InputEntity s);
}

其次 定义具体策略类,实现该抽象策略接口,具体的策略里包含对业务的封装

1
2
3
public class LocalAllCreditChinaFieldClean implements IBaseFieldCleanStrategy {
//todo
}

然后,定义上下文的引用

1
2
3
4
5
6
7
8
9
10
11
12
13
public class BaseContext {

public List<OutpuEntity> doClean(InputEntity sp) {
if (CollectionUtils.isNotEmpty(sp.getCustomsHeadquarters())) {
return BaseFactory.getType(MyType.HGZS.getValue()).doCleanFieldAlg(sp);
}

if (CollectionUtils.isNotEmpty(sp.getFinancePunishEntities())) {
return BaseFactory.getType(MyType.YH.getValue()).doCleanFieldAlg(sp);
}
return Collections.emptyList();
}
}

然后,抽象工厂初始化策略类到map里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BaseFactory {
private static Map<String, IBaseStrategy> strategyMap = new HashMap<>();

static {
strategyMap.put(PunishType.HGJCK.getValue(), new SourceHandlerV1());
strategyMap.put(PunishType.HGZS.getValue(), new SourceHandlerV2());
strategyMap.put(PunishType.YH.getValue(), new SourceHandlerV3());
strategyMap.put(PunishType.SJJXYZG.getValue(), new SourceHandlerV4());
}

public static IBaseStrategy getType(String type) {
return strategyMap.get(type);
}
}

枚举类封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public enum MyType {
HGJCK(1,"source1"),
HGZS(2,"source2"),
YH(3,"source3"),
SJXYZG(4,"source4")
;

private int code;
private String value;

PunishType(int code, String value) {
this.code = code;
this.value = value;
}

public int getCode() {
return code;
}

public String getValue() {
return value;
}
}

最后,在入口的FlatMap中,直接创建上下文类来调用策略方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ProcessingFlatMap implements FlatMapFunction<InputEntity,
OutputEntity> {
@Override
public void flatMap(InputEntity sp, Collector<OutputEntity> out) {
if (sp != null) {
List<OutputEntity> list = new BaseContext().doClean(sp);
if (CollectionUtils.isNotEmpty(list)) {
list.forEach(e -> {
if (e != null) {
out.collect(e);
}
});
}
}
}
}

总结

  • 策略模式主要的优点在于运行时切换策略使用,算法的实现和使用分离。符合开闭原则。

  • 若算法很少改动,此模式会徒增复杂,不合理。

  • 理解设计模式应该从实际的业务场景出发,搭好框架后不断优化细节。