首页 > 解决方案 > 模式观察者和 Spring

问题描述

我使用弹簧。我实现了模式观察者,如互联网上的示例所示。

@Component
public class ObserverManager {

    private Map<String, List<IObserver>> observers = new HashMap<>();

    public void subscribe(String type, IObserver observer){
        // do something
    }

    public void notifyObserver(String type, Object data){
        observers.get(type).forEach(observer -> observer.update(data));
    }

    // more
}
@Component
public class NewsObserver implements IObserver {
    @Override
    public void update(Object o) {
        //do something
    }
}

但我不明白如何正确地将观察者添加到 ObserverManager。它可以通过构造函数注入。但是,如果观察者是 7-8 呢?这对构造函数来说很多。如何正确解决这个问题?

标签: javaspringdesign-patternsobserver-pattern

解决方案


有很多方法可以解决这个问题,其中包括(可能更多):

观察员自己注册

解决这个问题的一个非常常见的方法是让观察者注册自己,这很干净,因为观察者确切地知道它想要观察什么。

@Component
public class NewsObserver implements IObserver {
    @Autowired
    public NewsObserver(IObserverManager observerManager) {
        observerManager.subscribe("interestingType", this);
    }

    @Override
    public void update(Object o) {
        //do something
    }
}

注入集合

另一种方法是List<IObserver>ObserverManager构造函数中使用 a :

@Autowired
public ObserverManager(List<IObserver> observers) {
    observersByType.put(type, observers);
}

但是这种类型从何而来?如果是观察者知道它,您必须首先迭代接收到的列表并从观察者那里获得正确的类型,然后用该类型注册它们中的每一个。

使用配置 Bean

@Component通过从 中删除NewsObserver并手动注册 bean,您可以执行以下操作:

@Bean
@Autowired
public IObserver newsObserver1(ObserverManager manager) {
    IObserver o1 = new NewsObserver();
    manager.subscribe("type1", o1);
    return o1;
}

@Bean
@Autowired
public IObserver newsObserver2(ObserverManager manager) {
    IObserver o2 = new NewsObserver();
    manager.subscribe("type2", o2);
    return o2;
}

选择什么?

我想这取决于。

IMO,更清洁的方法是第一个。ObserverManager请注意注入模拟进行测试是多么容易。还有谁能比观察者本身更了解观察者对什么类型的新闻感兴趣?观察者可以控制他们观察的内容和时间以及何时停止观察。

编辑:正如评论中正确指出的那样,这在观察者和观察者管理器之间引入了循环依赖。就目前而言,因为观察者管理器的实例没有保存在观察者中,并且您无法为同一个观察者实例调用两次观察者构造函数,所以循环永远不会发生。但这仍然是一种设计气味,可能会导致未来出现问题。

两个实体之间的循环依赖通常通过引入第三方来管理这两个实体之间的关系来解决,这导致我们找到了上述替代方案之一。

配置 bean 方法很有趣,因为在 中没有依赖关系NewsObserver,因此没有循环依赖关系,它只是坐在那里等待更新。不过需要注意的是,管理哪个观察者接收哪种类型以及何时停止订阅需要在观察者之外完成,这可能看起来不自然。

笔记:

作为旁注,请考虑使用 Multimap(例如可与Guava一起使用)作为观察者地图。Map它比of更容易管理List,但它需要额外的依赖,因为 Java 没有类似的内置类型。

此外,除非你真的想要,否则没有必要自己实现观察者模式。这些是它的真正强大的实现,并在它之上提供了非常好的操作:


推荐阅读