本文是我们名为“ Java设计模式 ”的学院课程的一部分。
在本课程中,您将深入研究大量的设计模式,并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因,并了解何时以及如何应用模式中的每一个。 在这里查看 !
目录
- 1.观察者模式 2.什么是观察者模式 3.实施观察者模式 4. Java内置的观察者模式 5.何时使用观察者模式 6.下载源代码
1.观察者模式
体育大厅是运动爱好者的理想运动场所。 它们涵盖几乎所有类型的体育活动,并提供最新新闻,信息,比赛预定日期,有关特定球员或球队的信息。 现在,他们计划提供实时评论或比赛分数作为SMS服务,但仅针对其高级用户。 他们的目标是在短时间间隔后发送即时比分,比赛情况和重要事件的短信。 作为用户,您需要订阅该程序包,并且在进行实时比赛时,您会收到有关实时评论的短信。 该站点还提供了一个随时取消订阅包的选项。
作为开发人员,运动大厅要求您为他们提供此新功能。 体育大厅的记者将坐在比赛的评论框中,并将实时评论更新为评论对象。 作为开发人员,您的工作是通过从评论对象(如果有)中获取评论来向注册用户提供评论。 进行更新时,系统应通过向订阅的用户发送SMS来更新他们。
这种情况清楚地显示了匹配项和用户之间的一对多映射,因为可能有许多用户订阅单个匹配项。 观察者设计模式最适合这种情况,让我们了解一下这种模式,然后为Sport Lobby创建功能。
2.什么是观察者模式
观察者模式是一种行为模式,与对象之间的职责分配有关。 行为模式表征了复杂的控制流,这些流在运行时很难遵循。 它们使您的注意力从控制流上移开,使您可以专注于对象的互连方式。
观察者模式定义了对象之间的一对多依赖关系,因此当一个对象改变状态时,其所有依赖关系都会得到通知并自动更新。 观察者模式描述了这些依赖关系。 此模式中的关键对象是主题和观察者。 主题可以具有任意数量的从属观察者。 只要对象的状态发生变化,就会通知所有观察者。 作为响应,每个观察者将查询主题以使其状态与主题状态同步。
了解观察者模式的另一种方法是发布者与订阅者之间的关系。 例如,假设您订阅了自己喜欢的体育或时尚杂志的杂志。 每当发行新期刊时,它就会交付给您。 如果您在不再需要该杂志时退订它,则该杂志将不会发送给您。 但是出版商继续像以前一样工作,因为还有其他人也订阅了该杂志。
观察者模式有四个参与者:
- 主题,用于注册观察者。 对象使用此接口注册为观察者,也将自己从观察者中删除。
- 观察者,定义了一个对象的更新接口,该对象应该在主题更改时得到通知。 所有观察者都需要实现Observer接口。 此接口具有
update()
方法,当Subject的状态更改时将调用该方法。 - ConcreteSubject,将感兴趣的状态存储到ConcreteObserver对象。 状态更改时,它将向其观察者发送通知。 具体主题始终实现Subject接口。 每当状态更改时,
notifyObservers()
方法用于更新所有当前观察者。 - ConcreateObserver,维护对ConcreteSubject对象的引用,并实现Observer接口。 每个观察者都向具体主题注册以接收更新。
3.实施观察者模式
让我们看看如何在开发运动大厅功能时使用观察者模式。 有人将更新具体主题的对象,而您的工作是更新在具体主题对象中注册的对象的状态。 因此,只要具体主体对象的状态发生变化,就应该通知其所有依赖对象,然后进行更新。
基于此,我们首先创建一个Subject
接口。 在“主题”界面中,有三种主要方法,可以根据需要选择添加其他一些方法(如果需要)。
package com.javacodegeeks.patterns.observerpattern;public interface Subject {public void subscribeObserver(Observer observer);public void unSubscribeObserver(Observer observer);public void notifyObservers();public String subjectDetails();
}
“ Subject
界面中的三个关键方法是:
-
subscribeObserver
,用于订阅观察者,或者我们可以说注册观察者,以便如果主题状态发生变化,则应通知所有这些观察者。 -
unSubscribeObserver
,用于取消订阅观察者,以便如果主题的状态发生变化,则不应通知此未订阅的观察者。 -
notifyObservers
,当主题状态发生变化时,此方法通知注册的观察者。
并且可选地,还有一个方法subjectDetails()
,这是一个简单的方法,并且可以根据您的需要。 在这里,其工作是返回主题的详细信息。
现在,让我们看看Observer
界面。
package com.javacodegeeks.patterns.observerpattern;public interface Observer {public void update(String desc);public void subscribe();public void unSubscribe();
}
- 当主题状态发生变化时,主题会在观察者上调用
update(String desc)
方法,以通知该方法。 -
subscribe()
方法用于订阅主题。 -
unsubscribe()
,方法用于取消订阅主题。
package com.javacodegeeks.patterns.observerpattern;public interface Commentary {public void setDesc(String desc);
}
报告者使用以上接口来更新评论对象的实时评论。 这是一个可选接口,仅用于遵循代码与接口的原则 ,与Observer模式无关。 您应该在适用的情况下应用oops原则以及设计模式。 该界面仅包含一种用于更改具体主题对象状态的方法。
package com.javacodegeeks.patterns.observerpattern;import java.util.List;public class CommentaryObject implements Subject,Commentary{private final List<Observer>observers;private String desc;private final String subjectDetails;public CommentaryObject(List<Observer>observers,String subjectDetails){this.observers = observers;this.subjectDetails = subjectDetails;}@Overridepublic void subscribeObserver(Observer observer) {observers.add(observer);}@Overridepublic void unSubscribeObserver(Observer observer) {int index = observers.indexOf(observer);observers.remove(index);}@Overridepublic void notifyObservers() {System.out.println();for(Observer observer : observers){observer.update(desc);}}@Overridepublic void setDesc(String desc) {this.desc = desc;notifyObservers();}@Overridepublic String subjectDetails() {return subjectDetails;}}
上面的类作为一个具体的主题,它实现Subject接口并提供其实现。 它还将引用存储到已注册的观察者。
package com.javacodegeeks.patterns.observerpattern;public class SMSUsers implements Observer{private final Subject subject;private String desc;private String userInfo;public SMSUsers(Subject subject,String userInfo){if(subject==null){throw new IllegalArgumentException("No Publisher found.");}this.subject = subject;this.userInfo = userInfo;}@Overridepublic void update(String desc) {this.desc = desc;display();}private void display(){System.out.println("["+userInfo+"]: "+desc);}@Overridepublic void subscribe() {System.out.println("Subscribing "+userInfo+" to "+subject.subjectDetails()+" ...");this.subject.subscribeObserver(this);System.out.println("Subscribed successfully.");}@Overridepublic void unSubscribe() {System.out.println("Unsubscribing "+userInfo+" to "+subject.subjectDetails()+" ...");this.subject.unSubscribeObserver(this);System.out.println("Unsubscribed successfully.");}}
上面的类是实现Observer
接口的具体观察者类。 它还存储对它订阅的主题的引用以及可选的用于显示用户信息的userInfo
变量。
现在,让我们测试示例。
package com.javacodegeeks.patterns.observerpattern;import java.util.ArrayList;public class TestObserver {public static void main(String[] args) {Subject subject = new CommentaryObject(new ArrayList<Observer>(), "Soccer Match [2014AUG24]");Observer observer = new SMSUsers(subject, "Adam Warner [New York]");observer.subscribe();System.out.println();Observer observer2 = new SMSUsers(subject, "Tim Ronney [London]");observer2.subscribe();Commentary cObject = ((Commentary)subject);cObject.setDesc("Welcome to live Soccer match");cObject.setDesc("Current score 0-0");System.out.println();observer2.unSubscribe();System.out.println();cObject.setDesc("It's a goal!!");cObject.setDesc("Current score 1-0");System.out.println();Observer observer3 = new SMSUsers(subject, "Marrie [Paris]");observer3.subscribe();System.out.println();cObject.setDesc("It's another goal!!");cObject.setDesc("Half-time score 2-0");}}
上面的示例将产生以下输出:
Subscribing Adam Warner [New York] to Soccer Match [2014AUG24] ...
Subscribed successfully.Subscribing Tim Ronney [London] to Soccer Match [2014AUG24] ...
Subscribed successfully.[Adam Warner [New York]]: Welcome to live Soccer match
[Tim Ronney [London]]: Welcome to live Soccer match[Adam Warner [New York]]: Current score 0-0
[Tim Ronney [London]]: Current score 0-0Unsubscribing Tim Ronney [London] to Soccer Match [2014AUG24] ...
Unsubscribed successfully.[Adam Warner [New York]]: It's a goal!![Adam Warner [New York]]: Current score 1-0Subscribing Marrie [Paris] to Soccer Match [2014AUG24] ...
Subscribed successfully.[Adam Warner [New York]]: It's another goal!!
[Marrie [Paris]]: It's another goal!![Adam Warner [New York]]: Half-time score 2-0
[Marrie [Paris]]: Half-time score 2-0
如您所见,起初有两个用户订阅了足球比赛并开始接收评论。 但是后来有一个用户取消了订阅,因此该用户没有再收到评论。 然后,另一个用户订阅并开始获取评论。
所有这一切都是动态发生的,无需更改现有代码,不仅如此,假设该公司是否要在电子邮件上广播评论,或者任何其他公司希望与该公司合作以广播评论。 您需要做的就是创建两个新类,例如UserEmail
和ColCompany
并通过实现Observer
接口使它们成为主题的Observer
。 据Subject
知道它是观察者,它将提供更新。
4. Java内置的观察者模式
Java内置了对Observer
模式的支持。 最通用的是java.util
包中的Observer接口和Observable类。 这些与我们的“主题和观察者”界面非常相似,但是为您提供了许多现成的功能。
让我们尝试使用Java的内置Observer模式实现上述示例。
package com.javacodegeeks.patterns.observerpattern;import java.util.Observable;public class CommentaryObjectObservable extends Observable implements Commentary {private String desc;private final String subjectDetails;public CommentaryObjectObservable(String subjectDetails){this.subjectDetails = subjectDetails;}@Overridepublic void setDesc(String desc) {this.desc = desc;setChanged();notifyObservers(desc);}public String subjectDetails() {return subjectDetails;}
}
这次,我们扩展了Observable
类,使我们的类成为一个主体,请注意,上面的类不包含对观察者的任何引用,它由父类处理,即Observable
类。 但是,我们声明了setDesc
方法来更改对象的状态,如上例所示。 setChanged
方法是上层类的方法,用于将更改的标志设置为true。 notifyObservers
方法通知其所有观察者,然后调用clearChanged
方法以指示此对象不再更改。 每个观察者都有使用两个参数调用的update方法:一个observable
对象和arg
参数。
package com.javacodegeeks.patterns.observerpattern;import java.util.Observable;public class SMSUsersObserver implements java.util.Observer{private String desc;private final String userInfo;private final Observable observable;public SMSUsersObserver(Observable observable,String userInfo){this.observable = observable;this.userInfo = userInfo;}public void subscribe() {System.out.println("Subscribing "+userInfo+" to "+((CommentaryObjectObservable)(observable)).subjectDetails()+" ...");this.observable.addObserver(this);System.out.println("Subscribed successfully.");}public void unSubscribe() {System.out.println("Unsubscribing "+userInfo+" to "+((CommentaryObjectObservable)(observable)).subjectDetails()+" ...");this.observable.deleteObserver(this);System.out.println("Unsubscribed successfully.");}@Overridepublic void update(Observable o, Object arg) {desc = (String)arg;display();}private void display(){System.out.println("["+userInfo+"]: "+desc);}}
让我们讨论一些关键方法。
上面的类实现了Observer
接口,该接口具有一个关键方法update
,当主题调用notifyObservers
方法时会调用该方法。 update
方法采用一个Observable
对象和一个arg
作为参数。
addObserver
方法用于将观察者注册到主题,而deleteObserver
方法用于将观察者从主题列表中删除。
让我们测试一下这个例子。
package com.javacodegeeks.patterns.observerpattern;public class Test {public static void main(String[] args) {CommentaryObjectObservable obj = new CommentaryObjectObservable("Soccer Match [2014AUG24]");SMSUsersObserver observer = new SMSUsersObserver(obj, "Adam Warner [New York]");SMSUsersObserver observer2 = new SMSUsersObserver(obj,"Tim Ronney [London]");observer.subscribe();observer2.subscribe();System.out.println("------------------------------------------------------");obj.setDesc("Welcome to live Soccer match");obj.setDesc("Current score 0-0");observer.unSubscribe();obj.setDesc("It's a goal!!");obj.setDesc("Current score 1-0");}
}
上面的示例将产生以下输出:
Subscribing Adam Warner [New York] to Soccer Match [2014AUG24] ...
Subscribed successfully.
Subscribing Tim Ronney [London] to Soccer Match [2014AUG24] ...
Subscribed successfully.
------------------------------------------------------
[Tim Ronney [London]]: Welcome to live Soccer match
[Adam Warner [New York]]: Welcome to live Soccer match
[Tim Ronney [London]]: Current score 0-0
[Adam Warner [New York]]: Current score 0-0
Unsubscribing Adam Warner [New York] to Soccer Match [2014AUG24] ...
Unsubscribed successfully.
[Tim Ronney [London]]: It's a goal!!
[Tim Ronney [London]]: Current score 1-0
上面的类创建一个主题和两个观察者。 observer
的subscribe
方法将自己添加到主题观察者列表中。 然后setDesc
更改主题的状态,该主题调用setChanged
方法将更改标志设置为true,并通知观察者。 结果,调用了观察者的update
方法,该方法在内部对display
方法进行分类以显示结果。 后来,一位观察者unsubscribe
d,即从观察者列表中将其删除。 由于此原因,以后的评论未更新。
Java为观察者模式提供了内置功能,但是它也有其自身的缺点。 Observable
是一个类,您必须对其进行子类化。 这意味着您不能将Observable行为添加到已经扩展了另一个超类的现有类上。 这限制了重用潜力。 您甚至无法创建自己的实现,以与Java的内置Observer API配合使用。 Observable API中的某些方法受到保护。 这意味着除非已将Observable
子类setChange
否则无法调用setChange
类的方法。 而且,您甚至无法创建Observable
类的实例并将其与自己的对象组合在一起,因此必须子类化。 此设计违反了“优先继承而不是继承”的设计原则。
5.何时使用观察者模式
在以下任何一种情况下,请使用Observer模式:
- 当抽象具有两个方面时,一个方面依赖于另一个方面。 将这些方面封装在单独的对象中,可以使您分别进行更改和重用。
- 当更改一个对象需要更改其他对象时,您不知道需要更改多少个对象。
- 一个对象何时应该能够通知其他对象而无需假设这些对象是谁。 换句话说,您不希望这些对象紧密耦合。
6.下载源代码
这是“观察者模式”的一课。 您可以在此处下载源代码: ObserverPattern –第7课
翻译自: https://www.javacodegeeks.com/2015/09/observer-design-pattern.html