问题描述
因为要求查询时的高效率,所以不确定度的最小/最大绝对值在持久化时就已经计算好。
当计量单位维护的倍数变动时,需要重新计算相关的不确定度的绝对值。
过去的实现方式就是在相关的方法中调用重新计算,从而提高两者相互的耦合性。
在听了潘老师对于观察者模式的讲解之后,决定采用观察者模式实现此功能,来降低计量单位模块对不确定度模块的耦合性。
被观察者
一个类继承了java.util.Observable
类,该类就继承了可订阅可通知的相关方法,该类的实例就成为了可被观察的对象。
@Service
public class MeasurementUnitServiceImpl extends Observable implements MeasurementUnitService {
private static final Logger logger = LoggerFactory.getLogger(MeasurementUnitServiceImpl.class);
@Autowired
private MeasurementUnitRepository measurementUnitRepository; // 计量单位
@Autowired
private MeasurementUnitCategoryService measurementUnitCategoryService; // 计量单位类别
@Override
@Transactional
public void update(Long id, MeasurementUnit measurementUnit) {
......
无关业务代码
......
/**
* 设置有变动
* 通知所有观察者
*/
this.setChanged();
this.notifyObservers(oldMeasurementUnit);
}
}
Java
中只要涉及到继承,我们就需要慎重考虑,因为Java
是单继承,这里直接继承就没问题了,如果该类已经有父类,且父类的代码我们无法变更,如果实现被观察者呢?
在网上看了多篇关于无法继承再实现观察者的博客,发现实现得都不是很优雅,就是把原来的java.util.Observable
中的相关方法抄过来。然后在接口中添加默认实现,然后实现组合,就有相应的方法了,是一种利用Java8
中接口的新特性变向实现继承的方法。
我还是不喜欢这种实现方式,毕竟这样写就把思想写死了,我们想寻求一种独特的不依赖于语言特性的方式。毕竟这样,思维才能提升。
直接通知
现在观察者需要继承java.util.Observable
,是因为其内部有相关的订阅与通知方法,这种实现,是基于被观察者直接通知观察者。
间接通知
又新开了好多书店,不同的书店有不同的杂志,每个卖家都要去实现一个可订阅的功能。
这样,每个卖家都需要去存一个订阅者列表,也就是每个被观察者都要去继承java.util.Observable
。
卖家觉得这种维护列表并通知的方式太麻烦,就使用了一款订阅软件,读者可以在订阅软件上订购卖家,然后卖家有新杂志了就去通知软件,然后软件再给给卖家的订阅读者发短信。
这样,就解决了卖家既需要通知读者,又不需要维护读者列表的问题。
在代码中体现,就是当前类不需要去继承java.util.Observable
,当前类通知一个维护关系的“中间对象”,该“中间对象”中维护相关的卖家与读者之间的维护列表,然后根据列表去通知相关对象。
中间对象
这个“中间对象”是哪来的呢?其实Spring
已经很好地为我们解决了这个问题,就是常说的事件监听。这是观察者模式的另一种实现。
- 自定义事件,自定义对该事件的监听。
- 在相关的方法中发布该事件。
- 上下文会根据事件与监听之间的对象关系进行事件通知。
观察者
实现Observer
接口中的update
方法,并订阅被观察者,就实现了一个观察者。
因为@Autowired
注入来的是接口类型(注入接口主要是考虑依赖倒置原则与Spring AOP
中使用的动态代理机制),而要订阅的被观察者对应的是类,所以需要在init
中判断该对象是否是MeasurementUnitServiceImpl
类的实例,并强转订阅。
@Service
public class AccuracyUncertaintyServiceImpl implements AccuracyUncertaintyService, Observer {
private static final Logger logger = Logger.getLogger(AccuracyUncertaintyServiceImpl.class);
@Autowired
private AccuracyUncertaintyRepository accuracyUncertaintyRepository; // 不确定度
@Autowired
private MeasurementUnitService measurementUnitService; // 计量单位
@Autowired
private ParameterCategoryService parameterCategoryService; // 参量类别
// 在Bean创建后执行,在构造函数之后
@PostConstruct
public void init() {
// 如果当前注入的MeasurementUnitService是类MeasurementUnitServiceImpl的实例
if (measurementUnitService instanceof MeasurementUnitServiceImpl) {
// 强转,并订阅观察者
MeasurementUnitServiceImpl measurementUnitServiceImpl = (MeasurementUnitServiceImpl) measurementUnitService;
measurementUnitServiceImpl.addObserver(this);
}
}
/**
* 注意,此处的update是实现观察者模式相关接口Observer的update方法
* @param o 被观察者
* @param arg 参数
*/
@Override
public void update(Observable o, Object arg) {
// 如果是计量单位Service发送的通知信息并且参数为计量单位
if (o instanceof MeasurementUnitServiceImpl) {
if (arg instanceof MeasurementUnit) {
......
执行与本文无关业务逻辑
......
}
}
}
}
总结
设计模式,是一代代软件工程师对软件开发中快速的需求变更总结出的一套最佳实践。理解前辈们的思想,理解实际的业务场景,才能更好地应用。
要实现一个功能,不止在代码层面思考,更重要的是在思想层面去思考。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。