让我们首先考虑下面的场景来理解观察者模式。
脚本 :
假设我们正在构建一个板球应用程序,它会通知观众有关当前分数、跑步率等信息。假设我们制作了两个显示元素CurrentScoreDisplay和AverageScoreDisplay。CricketData拥有所有数据(跑步、保龄球等),每当数据更改时,显示元素都会收到新数据的通知,并相应地显示最新数据。
下面是这个设计的java实现。
// Java implementation of above design for Cricket App. The // problems with this design are discussed below. // A class that gets information from stadium and notifies // display elements, CurrentScoreDisplay & AverageScoreDisplay class CricketData { int runs, wickets; float overs; CurrentScoreDisplay currentScoreDisplay; AverageScoreDisplay averageScoreDisplay; // Constructor public CricketData(CurrentScoreDisplay currentScoreDisplay, AverageScoreDisplay averageScoreDisplay) { this .currentScoreDisplay = currentScoreDisplay; this .averageScoreDisplay = averageScoreDisplay; } // Get latest runs from stadium private int getLatestRuns() { // return 90 for simplicity return 90 ; } // Get latest wickets from stadium private int getLatestWickets() { // return 2 for simplicity return 2 ; } // Get latest overs from stadium private float getLatestOvers() { // return 10.2 for simplicity return ( float ) 10.2 ; } // This method is used update displays when data changes public void dataChanged() { // get latest data runs = getLatestRuns(); wickets = getLatestWickets(); overs = getLatestOvers(); currentScoreDisplay.update(runs,wickets,overs); averageScoreDisplay.update(runs,wickets,overs); } } // A class to display average score. Data of this class is // updated by CricketData class AverageScoreDisplay { private float runRate; private int predictedScore; public void update( int runs, int wickets, float overs) { this .runRate = ( float )runs/overs; this .predictedScore = ( int ) ( this .runRate * 50 ); display(); } public void display() { System.out.println( "Average Score Display:" + "Run Rate: " + runRate + "PredictedScore: " + predictedScore); } } // A class to display score. Data of this class is // updated by CricketData class CurrentScoreDisplay { private int runs, wickets; private float overs; public void update( int runs, int wickets, float overs) { this .runs = runs; this .wickets = wickets; this .overs = overs; display(); } public void display() { System.out.println( "Current Score Display: " + "Runs: " + runs + "Wickets:" + wickets + "Overs: " + overs ); } } // Driver class class Main { public static void main(String args[]) { // Create objects for testing AverageScoreDisplay averageScoreDisplay = new AverageScoreDisplay(); CurrentScoreDisplay currentScoreDisplay = new CurrentScoreDisplay(); // Pass the displays to Cricket data CricketData cricketData = new CricketData(currentScoreDisplay, averageScoreDisplay); // In real app you would have some logic to call this // function when data changes cricketData.dataChanged(); } } |
输出:
Current Score Display: Runs: 90 Wickets:2 Overs: 10.2 Average Score Display: Run Rate: 8.823529 PredictedScore: 441
以上设计的问题 :
- CricketData保存对具体显示元素对象的引用,即使它只需要调用这些对象的更新方法。它可以访问太多超出其要求的额外信息。
- 此语句“currentScoreDisplay.update(runs、wickets、overs);”违反了最重要的设计原则之一“程序到接口,而不是实现”因为我们使用具体的对象来共享数据,而不是抽象的接口。
- 数据和显示元素紧密耦合。
- 如果将来出现另一个需求,我们需要添加另一个显示元素,我们需要对代码的非变化部分(数据)进行更改。这绝对不是一个好的设计实践,应用程序可能无法处理更改,也不容易维护。
如何避免这些问题? 使用观察者模式
观察者模式
要理解观察者模式,首先需要理解主体和观察者对象。
主体和观察者之间的关系可以很容易地理解为类似于杂志订阅。
- 杂志出版商(主题)从事出版杂志(数据)的业务。
- 如果你(数据用户/观察者)对你订阅(注册)的杂志感兴趣,并且如果新版本出版,它会被交付给你。
- 如果你取消订阅(取消注册),你将停止获得新版本。
- Publisher不知道你是谁,也不知道你是如何使用杂志的,它只是把杂志发给你,因为你是订阅者(松散耦合)。
定义:
观察者模式定义了对象之间的一对多依赖关系,以便一个对象改变状态,其所有依赖项都会被自动通知和更新。
说明:
- 一对多依赖是主体(一)和观察者(多)之间的依赖。
- 由于观察者本身无法访问数据,因此存在依赖性。他们依靠受试者向他们提供数据。
类图:
图片来源: 维基百科
- 这里Observer和Subject是接口(可以是任何抽象超类型,不一定是java接口)。
- 所有需要数据的观察者都需要实现观察者接口。
- observer接口中的notify()方法定义了主体提供数据时要采取的操作。
- 主体维护一个observerCollection,它只是当前注册(订阅)的观察者列表。
- registerObserver(观察者)和unregisterObserver(观察者)分别是添加和删除观察者的方法。
- 当数据发生更改并且需要向观察者提供新数据时,将调用notifyObservators()。
优势: 在交互对象之间提供松散耦合的设计。松散耦合的对象可以灵活地适应不断变化的需求。在这里,松耦合意味着相互作用的对象应该彼此拥有较少的信息。
观察者模式提供了这种松散耦合:
- 主体只知道观察者实现了观察者接口。没别的了。
- 不需要修改主题来添加或删除观察者。
- 我们可以独立地重用subject和observer类。
缺点:
- 内存泄漏是由 听者失误问题 因为观察员的明确注册和注销。
什么时候使用这种模式? 当多个对象依赖于一个对象的状态时,应考虑在该应用程序中使用此模式,因为它提供了一个整洁且经过良好测试的设计。
现实生活中的用途:
- 它在GUI工具包和事件侦听器中大量使用。在java中,按钮(subject)和onClickListener(observer)是用observer模式建模的。
- 社交媒体、RSS源、电子邮件订阅,您可以选择关注或订阅,并收到最新通知。
- play store上应用程序的所有用户都会收到更新通知。
进一步阅读—— Python中的观察者方法
本文由 苏拉布·库马尔 .如果你喜欢GeekSforgek,并想贡献自己的力量,你也可以写一篇文章,然后将文章邮寄给评论-team@geeksforgeeks.org.看到你的文章出现在Geeksforgeks主页上,并帮助其他极客。
如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请写评论