5

问题描述

因为要求查询时的高效率,所以不确定度的最小/最大绝对值在持久化时就已经计算好。

clipboard.png

当计量单位维护的倍数变动时,需要重新计算相关的不确定度的绝对值。

过去的实现方式就是在相关的方法中调用重新计算,从而提高两者相互的耦合性。

在听了潘老师对于观察者模式的讲解之后,决定采用观察者模式实现此功能,来降低计量单位模块对不确定度模块的耦合性。

被观察者

一个类继承了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,是因为其内部有相关的订阅与通知方法,这种实现,是基于被观察者直接通知观察者。

clipboard.png

间接通知

又新开了好多书店,不同的书店有不同的杂志,每个卖家都要去实现一个可订阅的功能。

clipboard.png

这样,每个卖家都需要去存一个订阅者列表,也就是每个被观察者都要去继承java.util.Observable

卖家觉得这种维护列表并通知的方式太麻烦,就使用了一款订阅软件,读者可以在订阅软件上订购卖家,然后卖家有新杂志了就去通知软件,然后软件再给给卖家的订阅读者发短信。

clipboard.png

clipboard.png

这样,就解决了卖家既需要通知读者,又不需要维护读者列表的问题。

在代码中体现,就是当前类不需要去继承java.util.Observable,当前类通知一个维护关系的“中间对象”,该“中间对象”中维护相关的卖家与读者之间的维护列表,然后根据列表去通知相关对象。

中间对象

这个“中间对象”是哪来的呢?其实Spring已经很好地为我们解决了这个问题,就是常说的事件监听。这是观察者模式的另一种实现。

clipboard.png

  1. 自定义事件,自定义对该事件的监听。
  2. 在相关的方法中发布该事件。
  3. 上下文会根据事件与监听之间的对象关系进行事件通知。

观察者

实现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) {

                ......
                执行与本文无关业务逻辑
                ......
            }
        }
    }
}

总结

设计模式,是一代代软件工程师对软件开发中快速的需求变更总结出的一套最佳实践。理解前辈们的思想,理解实际的业务场景,才能更好地应用。

要实现一个功能,不止在代码层面思考,更重要的是在思想层面去思考。


张喜硕
2.1k 声望423 粉丝

浅梦辄止,书墨未浓。