java - 模式观察者和 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 呢?这对构造函数来说很多。如何正确解决这个问题?
解决方案
有很多方法可以解决这个问题,其中包括(可能更多):
观察员自己注册
解决这个问题的一个非常常见的方法是让观察者注册自己,这很干净,因为观察者确切地知道它想要观察什么。
@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 没有类似的内置类型。
此外,除非你真的想要,否则没有必要自己实现观察者模式。这些是它的真正强大的实现,并在它之上提供了非常好的操作:
推荐阅读
- javascript - 尝试通过 Vue.js 中的 API 控制 Plyr
- spss - 如何在 SPSS 中从单独的月份和年份变量创建单个日期变量
- php - Ajax 返回 HTML 代码和我的预期输出
- javascript - 使用 servlet 生成 HTML 表格
- python - 虚拟环境应该驻留在 envs 文件夹中吗?
- swiftui - 给出的错误消息:无法推断通用参数“数据”
- android - 如何在原生 android 2019 中使用 WebRTC
- c++ - 委托的 ctor 是否受参数评估顺序的影响?
- c# - 哪种解决方案最适合打破用户输入的循环?
- python - PYTHON:如何重复任务?