java 观察者模式示例_观察者设计模式示例

java 观察者模式示例

本文是我们名为“ Java设计模式 ”的学院课程的一部分。

在本课程中,您将深入研究大量的设计模式,并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因,并了解何时以及如何应用模式中的每一个。 在这里查看

1.观察者模式

体育大厅是运动爱好者的理想运动场所。 它们涵盖几乎所有类型的体育活动,并提供最新新闻,信息,比赛预定日期,有关特定球员或球队的信息。 现在,他们计划提供实时评论或比赛分数作为SMS服务,但仅针对其高级用户。 他们的目标是在短时间间隔后通过短信发送实时比分,比赛情况和重要事件。 作为用户,您需要订阅该程序包,并且在进行实时比赛时,您会收到一条有关实时评论的短信。 该站点还提供了一个随时取消订阅包的选项。

作为开发人员,运动大厅要求您为他们提供此新功能。 体育大厅的记者将坐在比赛中的评论框内,并将实时评论更新为评论对象。 作为开发人员,您的工作是通过从评论对象(如果有)中获取评论来向注册用户提供评论。 进行更新时,系统应通过向订阅的用户发送SMS来更新他们。

这种情况清楚地显示了比赛与用户之间的一对多映射,因为可能有许多用户订阅单个比赛。 观察者设计模式最适合这种情况,让我们了解一下这种模式,然后为Sport Lobby创建功能。

2.什么是观察者模式

观察者模式是一种行为模式,它与对象之间的职责分配有关。 行为模式表征了复杂的控制流,这些流在运行时很难遵循。 它们将您的注意力从控制流上移开,让您仅专注于对象的互连方式。

观察者模式定义了对象之间的一对多依赖关系,因此当一个对象改变状态时,其所有依赖关系都会得到通知并自动更新。 观察者模式描述了这些依赖关系。 此模式中的关键对象是主题和观察者。 主题可以具有任意数量的从属观察者。 只要对象的状态发生变化,就会通知所有观察者。 作为响应,每个观察者将查询主题以使其状态与主题状态同步。

了解观察者模式的另一种方法是发布者与订阅者之间的关系。 例如,假设您订阅了自己喜欢的体育或时尚杂志的杂志。 每当发行新期刊时,它就会交付给您。 如果您在不再需要该杂志时取消订阅,则该杂志将不会发送给您。 但是出版商继续像以前一样工作,因为还有其他人也订阅了该杂志。

类图1

图1

观察者模式有四个参与者:

  1. 主题,用于注册观察者。 对象使用此接口注册为观察者,也将自己从观察者中删除。
  2. 观察者,定义了一个对象的更新接口,该对象应该在主题更改时得到通知。 所有观察者都需要实现Observer接口。 该接口具有update()方法,当Subject的状态更改时会调用该方法。
  3. ConcreteSubject,将感兴趣的状态存储到ConcreteObserver对象。 状态更改时,它将向其观察者发送通知。 具体主题始终实现Subject接口。 每当状态更改时, notifyObservers()方法用于更新所有当前观察者。
  4. ConcreateObserver,维护对ConcreteSubject对象的引用,并实现Observer接口。 每个观察者都向具体主题注册以接收更新。

3.实施观察者模式

让我们看看如何在开发运动大厅功能时使用观察者模式。 有人将更新具体主题的对象,而您的工作是更新在具体主题对象中注册的对象的状态。 因此,只要具体主体对象的状态发生变化,就应该通知其所有依赖对象,然后进行更新。

类图2

图2

基于此,我们首先创建一个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界面中的三个关键方法是:

  1. subscribeObserver ,用于订阅观察者,或者我们可以说注册观察者,以便如果主题的状态发生变化,则应通知所有这些观察者。
  2. unSubscribeObserver ,用于取消订阅观察者,因此,如果主题的状态发生变化,则不应通知该未订阅的观察者。
  3. notifyObservers ,当主题状态发生变化时,此方法通知注册的观察者。

并且可选地,还有一个方法subjectDetails() ,这是一个简单的方法,并且根据您的需要。 在这里,其工作是返回主题的详细信息。

现在,让我们看看Observer界面。

package com.javacodegeeks.patterns.observerpattern;

public interface Observer {

	public void update(String desc);
	public void subscribe();
	public void unSubscribe();
}
  1. 当主题状态发生变化时,主题会在观察者上调用update(String desc)方法,以通知该方法。
  2. subscribe() ,方法用于向主题订阅自身。
  3. 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;
	}
	@Override
	public void subscribeObserver(Observer observer) {
		observers.add(observer);
	}

	@Override
	public void unSubscribeObserver(Observer observer) {
		int index = observers.indexOf(observer);
		observers.remove(index);

	}

	@Override
	public void notifyObservers() {
		System.out.println();
		for(Observer observer : observers){
			observer.update(desc);
		}

	}

	@Override
	public void setDesc(String desc) {
		this.desc = desc;
		notifyObservers();
	}
	@Override
	public 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;
	}

	@Override
	public void update(String desc) {
		this.desc = desc;
		display();
	}

	private void display(){
		System.out.println("["+userInfo+"]: "+desc);
	}

	@Override
	public void subscribe() {
		System.out.println("Subscribing "+userInfo+" to "+subject.subjectDetails()+" ...");
		this.subject.subscribeObserver(this);
		System.out.println("Subscribed successfully.");
	}

	@Override
	public 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-0

Unsubscribing Tim Ronney [London] to Soccer Match [2014AUG24] ...
Unsubscribed successfully.

[Adam Warner [New York]]: It's a goal!!

[Adam Warner [New York]]: Current score 1-0

Subscribing 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

如您所见,起初有两个用户订阅了足球比赛并开始接收评论。 但是后来有一个用户取消了订阅,因此该用户没有再收到评论。 然后,另一个用户订阅并开始获取评论。

所有这一切都是动态发生的,无需更改现有代码,不仅如此,假设该公司是否要在电子邮件上广播评论,或者任何其他公司希望与该公司进行合作以广播评论。 您需要做的就是创建两个新类,例如UserEmailColCompany并通过实现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;
	}

	@Override
	public 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.");
	}

	@Override
	public 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

上面的类创建一个主题和两个观察者。 observersubscribe方法将其自身添加到主题观察者列表中。 然后setDesc更改主题的状态,该主题调用setChanged方法将更改标志设置为true,并通知观察者。 结果,调用了观察者的update方法,该方法在内部对display方法进行分类以显示结果。 后来,一位观察者unsubscribe d,即从观察者列表中将其删除。 由于此原因,以后的评论未更新。

Java为观察者模式提供了内置功能,但是它也有其自身的缺点。 Observable是一个类,您必须对其进行子类化。 这意味着您不能将Observable行为添加到已经扩展了另一个超类的现有类上。 这限制了重用潜力。 您甚至无法创建自己的实现,以与Java的内置Observer API配合使用。 Observable API中的某些方法受到保护。 这意味着除非已将Observable子类setChange否则无法调用setChange类的方法。 而且,您甚至无法创建Observable类的实例并将其与自己的对象组合在一起,因此必须子类化。 此设计违反了“优先继承而不是继承”的设计原则。

5.何时使用观察者模式

在以下任何一种情况下,请使用Observer模式:

  1. 当抽象具有两个方面时,一个方面依赖于另一个方面。 将这些方面封装在单独的对象中,使您可以分别更改和重用它们。
  2. 当更改一个对象需要更改其他对象时,您不知道需要更改多少个对象。
  3. 一个对象何时应该能够通知其他对象而无需假设这些对象是谁。 换句话说,您不希望这些对象紧密耦合。

6.下载源代码

这是“观察者模式”的一课。 您可以在此处下载源代码: ObserverPattern –第7课

翻译自: https://www.javacodegeeks.com/2015/09/observer-design-pattern.html

java 观察者模式示例

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值