Java服务_观察者模式实现不同维度过滤类型解析

Java服务_观察者模式实现不同维度过滤类型解析

1.观察者模式简介

如我自己的博文【基础知识】_设计模式所述,观察者模式是定义对象间的一对多依赖关系,使得当一个被观察者对象状态发生改变时,多个观察者对象皆得到通知并被自动更新。

观察者模式最大的优点就是实现层和逻辑层的分离,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色;且符合开闭原则(对拓展开放,对修改关闭)的要求。

2.使用场景

在指标服务中处理统一协议请求时,需要将不同过滤类型的过滤条件UCriterion解析为相同的DriveFilterBO对象。

过滤条件抽象接口UCriterion如下:

1
2
3
4
5
public interface UCriterion extends Serializable {
String BLANK_PLACEHOLDER = "&a@b#c%";

String toSqlString();
}

其有多个实现类,如下:

1
2
3
4
5
6
7
public class SimpleExpression implements UExpression {
private static final long serialVersionUID = -4855169404834444045L;
private String propertyName;
private final Object value;
private final String op;
private final String type;
}
1
2
3
4
5
6
7
public class InExpression implements UExpression {
private static final long serialVersionUID = 8692451650339719589L;
private String propertyName;
private Object[] values;
private static final String op = "in";
private String type;
}

过滤条件通用对象DriveFilterBO如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DriveFilterBO {

/**
* 维度ID
*/
private Long dimensionDefId;

/**
* 数据操作符
*/
private DriveMathOperatorEnum mathOp;

/**
* 值列表
*/
private List<String> valueList;
}

3.使用观察者模式

3.1 创建被观察者类

1)executorMap属性用于保存观察者实例列表,key为观察者类名简称,value为观察者对象,注意使用线程安全的map;

2)attach()为添加观察者方法,应该以每个观察者对象为入参分别调用该方法一次;

3)convert()为消息发送方法,此处逻辑为每次调用该方法时遍历所有观察者获取指定观察者对象,执行其convert()方法。

很明显,创建这个观察者模式就是为了将convert()方法抽象出来,对不同的UCriterion创建了不同的观察者来实现不同的解析逻辑。(此处使用观察者模式最大的优点就是扩展观察者非常方便,不用修改原逻辑,其实使用工厂模式也可以达到相同的目的)

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
29
30
31
32
33
34
@Slf4j
@Component
public class UCriterionConverterManager {

public static final Logger LOGGER = LoggerFactory.getLogger(UCriterionConverterManager.class);

private Map<String, AbstractUCriterionConverter> executorMap = Collections.synchronizedMap(new HashedMap());

/**
* 观察这模式:添加一个观察者
* @param converter 观察者实例对象
*/
public void attach(AbstractUCriterionConverter converter) {
Class typeClz = converter.getTypeClz();
//获取类名字符串
String simpleName = typeClz.getSimpleName();
executorMap.put(simpleName, converter);
}

/**
* 对不同的过滤条件做统一的转换
*/
public DriveFilterBO convert(UCriterion criterion) {
if (criterion == null) {
throw new RuntimeException("the input of criterion can not be null.");
}
String simpleName = criterion.getClass().getSimpleName();
if (!executorMap.containsKey(simpleName)) {
throw new RuntimeException("the executorMap of UCriterionConverterManager don't contain the adapter convert. clzType = " + simpleName);
}
AbstractUCriterionConverter converter = executorMap.get(simpleName);
return converter.convert(criterion);
}
}

3.2 创建观察者抽象类

1)在抽象观察者中创建抽象属性和方法;

2)创建构造方法,在构造方法中调用attach()添加一个观察者方法,这一步结合观察者实现类中的构造方法来看很妙。

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
29
30
@Slf4j
public abstract class AbstractUCriterionConverter<T extends UCriterion> {
/**
* 可以处理的类
*/
final Class<T> typeClz;

/**
* 观察者
*/
protected UCriterionConverterManager manager;

/**
* 公用的维度类
*/
protected UnifyDimDefService unifyDimDefService;

public AbstractUCriterionConverter(Class<T> typeClz, UCriterionConverterManager manager, UnifyDimDefService unifyDimDefService) {
this.typeClz = typeClz;
this.manager = manager;
this.unifyDimDefService = unifyDimDefService;
this.manager.attach(this);
}

public abstract DriveFilterBO convert(T criterion);

public Class<T> getTypeClz() {
return typeClz;
}
}

3.3 创建观察者实现子类

1)在不同观察者实现类中编写了个性化的convert()方法实现逻辑;

2)此处将@Autowired加在构造方法上,非常巧妙,@Autowired加在方法上时会将方法入参对应的依赖对象注入该方法,并完成自动装配。

3)此处将@Autowired加在构造方法上,同时该实现类添加@Component注解实现单例模式,则只会在Spring容器启动时执行一次该构造方法,且该改造方法调用父类构造方法从而调用attach()添加自己作为一个观察者。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
public class SimpleExpressionConverter extends AbstractUCriterionConverter<SimpleExpression> {

public static final Logger LOGGER = LoggerFactory.getLogger(SimpleExpressionConverter.class);

@Autowired
public SimpleExpressionConverter(UCriterionConverterManager manager, UnifyDimDefService unifyDimDefService) {
super(SimpleExpression.class, manager, unifyDimDefService);
}

@Override
public DriveFilterBO convert(SimpleExpression criterion) {
DriveFilterBO filterBO = new DriveFilterBO();
filterBO.setMathOp(DriveMathOperatorEnum.of(criterion.getOp()));
filterBO.setDimensionDefId(getDimIdByName(criterion.getPropertyName()));
List<String> valueList = new ArrayList<>();
valueList.add(String.valueOf(criterion.getValue()));
filterBO.setValueList(valueList);
return filterBO;
}
}
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
@Component
@Slf4j
public class InExpressionConverter extends AbstractUCriterionConverter<InExpression> {

public static final Logger LOGGER = LoggerFactory.getLogger(InExpressionConverter.class);

public static final DriveMathOperatorEnum opEnum = DriveMathOperatorEnum.IN;

@Autowired
public InExpressionConverter(UCriterionConverterManager manager, UnifyDimDefService unifyDimDefService) {
super(InExpression.class, manager, unifyDimDefService);
}

@Override
public DriveFilterBO convert(InExpression criterion) {
DriveFilterBO filterBO = new DriveFilterBO();
filterBO.setMathOp(opEnum);
filterBO.setDimensionDefId(getDimIdByName(criterion.getPropertyName()));
List<String> valueList = new ArrayList<>();
Object[] values = criterion.getValues();
for (Object item : values) {
valueList.add(String.valueOf(item));
}
filterBO.setValueList(valueList);
return filterBO;
}
}

3.4 被观察者实例发送消息

使用被观察者实例的convert()消息发送方法处理不用的过滤条件UCriterion对象:

1
2
3
4
for (UCriterion item : criterionList) {
DriveFilterBO convert = uCriterionConverterManager.convert(item);
filterList.add(convert);
}