软件设计模式之观察者模式
观察者模式定义了对象之间的一对多依赖关系,以便一个对象改变状态,其所有依赖关系都会得到通知并自动更新。
场景:
假设我们正在构建一个板球应用程序,该程序可以向观众通知诸如当前得分,跑步率等信息。假设我们已经创建了两个显示元素CurrentScoreDisplay和AverageScoreDisplay。CricketData拥有所有数据(行程,碗等),每当数据更改时,显示元素就会收到新数据的通知,并相应地显示最新数据。
Output:
Current Score Display: Runs: 90 Wickets:2 Overs: 10.2 Average Score Display: Run Rate: 8.823529 PredictedScore: 441
以上设计存在的问题:
CricketData保留对具体显示元素对象的引用,即使它仅需要调用这些对象的update方法。它有权访问过多的其他信息。
这条语句“ currentScoreDisplay.update(runs,wickets,overs);” 违反了最重要的设计原则之一“程序是接口,而不是实现”。因为我们正在使用具体对象共享数据而不是抽象接口。
CricketData和显示元素紧密耦合。
如果将来有另一个要求,并且需要添加另一个显示元素,则需要对代码的不变部分(CricketData)进行更改。这绝对不是一个好的设计实践,并且应用程序可能无法处理更改并且不易于维护。
如何避免这些问题?要了解观察者模式,首先需要了解主题和观察者对象。
主题和观察者之间的关系可以很容易地理解为类似于杂志订阅。
杂志出版商(主题)在业务中,并出版杂志(数据)。
如果您(数据用户/观察者)对您订阅(注册)的杂志感兴趣,并且如果发布了新版本,它将发送给您。
如果您取消订阅(取消注册),则停止获取新版本。
发行人不知道您是谁,以及如何使用该杂志,它只是将其提供给您,因为您是订户(松耦合)。
定义:
观察者模式定义了对象之间的一对多依赖关系,以便一个对象改变状态,其所有依赖关系都会得到通知并自动更新。
说明:
主题(一个)和观察者(许多)之间存在一对多的依赖关系。
存在依赖性,因为观察者本身无权访问数据。他们依赖于主题提供数据。
类图:
这里的Observer和Subject是接口(可以是任何抽象超类型,不一定是java接口)。
所有需要数据的观察者都需要实现观察者接口。
观察者接口中的notify()方法定义主题提供数据时要采取的措施。
该主题维护一个observerCollection,它只是当前已注册(订阅)的观察者的列表。
registerObserver(observer)和unregisterObserver(observer)是分别添加和删除观察者的方法。
当数据更改并且需要向观察者提供新数据时,将调用notifyObservers()。
优点:
在交互的对象之间提供松散耦合的设计。松散耦合的对象可以随需求的变化而灵活变化。在这里,松散耦合意味着相互作用的对象彼此之间的信息较少。
观察者模式提供了这种松散耦合:
主体只知道观察者实现了Observer接口,仅此而已。
无需修改Subject即可添加或删除观察者。
我们可以彼此独立地重用主题和观察者类。
缺点:
由于观察者的显式注册和取消注册,导致侦听器问题导致的内存泄漏。
何时使用此模式?
当多个对象依赖于一个对象的状态时,您应该考虑在应用程序中使用此模式,因为它为同一对象提供了简洁且经过良好测试的设计。
现实生活中的用途:
它在GUI工具包和事件侦听器中大量使用。在Java中,button(主题)和onClickListener(observer)使用观察者模式建模。
社交媒体,RSS提要,电子邮件订阅,您可以选择关注或订阅,并且会收到最新通知。
Play商店中某个应用的所有用户都会收到更新通知。