设计模式-观察者(译)

172 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

观察者模式是一种行为设计模式。观察模式通常用于你对对象的状态感兴趣,并且希望在状态在任何改变时被通知。在观察者模式中,看着某一个对象的状态变化的我们成为观察者(observer),被看着的对象我们称为被观察者主体(Subject)

观察者设计模式

根据GOF,观察者模式的使用意图是;

在对象之间定义一个一堆多的依赖关系,当一个对象的状态变化发生变化时,其它所有依赖它的对象都会被通知和自动更新

Subject 包含一系列的观察者,当有状态变化时来通知他们,因此它需要提供一个方法来让观察者可以注册和解除注册。Subject也需要包含一个方法来通知变化给所有的观察者或者发生变更给观察者,或者它可以提供另一个方法让观察者来获取变更

观察者应该有一个方法来设置观察对象,和另一个方法来处理Subject对象通知他们变化

java里通过内置的 java.util.Observable 类和 java.util.Observer接口来实现观察者模式。可是它们并不是被广泛的使用,因为这种实现非常的简单并且在大部分时候我们不希望使用继承只是为了实现观察者模式,而Java本身不支持多继承

Java Message Service(JMS)使用了观察者模式。和中介者模式(mediator pattern)一起允许应用订阅和发布数据给其它应用

Model-View-Controller(MVC)框架也是使用了观察者模式,Model就是Subject对象,Views就是观察者可以订阅获取模型的任何变更通知

Java中观察者模式例子

我们来看一个java中的观察者模式的例子,我们将实现一个简单的主题(topic),观察者(observer)可以订阅这个主题,当有任何的消息被发送到这个主题,所有的注册者都将会被通知来消费这条消息

基于我们Subject的需求,这儿是和基础的subject接口来定义抽象方法,它将在构建subject时被实现

package com.journaldev.design.observer;

public interface Subject {

	//methods to register and unregister observers
	public void register(Observer obj);
	public void unregister(Observer obj);
	
	//method to notify observers of change
	public void notifyObservers();
	
	//method to get updates from subject
	public Object getUpdate(Observer obj);
	
}

接下来我们创建一个观察者接口,会有一个附加subject的方法,和另一个处理subject通知变化的方法

package com.journaldev.design.observer;

public interface Observer {
	
	//method to update the observer, used by subject
	public void update();
	
	//attach with subject to observe
	public void setSubject(Subject sub);
}

现在我们的抽象都准备好了,让我们来创建主题的实现

package com.journaldev.design.observer;

import java.util.ArrayList;
import java.util.List;

public class MyTopic implements Subject {

	private List<Observer> observers;
	private String message;
	private boolean changed;
	private final Object MUTEX= new Object();
	
	public MyTopic(){
		this.observers=new ArrayList<>();
	}
	@Override
	public void register(Observer obj) {
		if(obj == null) throw new NullPointerException("Null Observer");
		synchronized (MUTEX) {
		if(!observers.contains(obj)) observers.add(obj);
		}
	}

	@Override
	public void unregister(Observer obj) {
		synchronized (MUTEX) {
		observers.remove(obj);
		}
	}

	@Override
	public void notifyObservers() {
		List<Observer> observersLocal = null;
		//synchronization is used to make sure any observer registered after message is received is not notified
		synchronized (MUTEX) {
			if (!changed)
				return;
			observersLocal = new ArrayList<>(this.observers);
			this.changed=false;
		}
		for (Observer obj : observersLocal) {
			obj.update();
		}

	}

	@Override
	public Object getUpdate(Observer obj) {
		return this.message;
	}
	
	//method to post message to the topic
	public void postMessage(String msg){
		System.out.println("Message Posted to Topic:"+msg);
		this.message=msg;
		this.changed=true;
		notifyObservers();
	}

}

可以看到注册和取消注册observer非常的简单,这儿额外的方法postMessage()用于客户端程序发送一个String的消息给topic。可以看到用一个boolean变量来跟踪主题的状态变化并且在通知观察者的方法使用。这个变量的使用是为了如果没有任何消息变化时,调用了notifyObesrvers()这个方法也不会发送错误的通知给观察者们

另外在notifyObservers()中使用的线程同步器来确保通知被发送给那些消息发送前注册的监听者

下面就是观察者的实现

package com.journaldev.design.observer;

public class MyTopicSubscriber implements Observer {
	
	private String name;
	private Subject topic;
	
	public MyTopicSubscriber(String nm){
		this.name=nm;
	}
	@Override
	public void update() {
		String msg = (String) topic.getUpdate(this);
		if(msg == null){
			System.out.println(name+":: No new message");
		}else
		System.out.println(name+":: Consuming message::"+msg);
	}

	@Override
	public void setSubject(Subject sub) {
		this.topic=sub;
	}

}

可以看到update()方法的实现是调用了subject方法的getUpdate()方法来获取消息消费。我们可以避免传递消息参数给update()

下面是一个简单的测试程序来消费topic


package com.journaldev.design.observer;

public class ObserverPatternTest {

	public static void main(String[] args) {
		//create subject
		MyTopic topic = new MyTopic();
		
		//create observers
		Observer obj1 = new MyTopicSubscriber("Obj1");
		Observer obj2 = new MyTopicSubscriber("Obj2");
		Observer obj3 = new MyTopicSubscriber("Obj3");
		
		//register observers to the subject
		topic.register(obj1);
		topic.register(obj2);
		topic.register(obj3);
		
		//attach observer to subject
		obj1.setSubject(topic);
		obj2.setSubject(topic);
		obj3.setSubject(topic);
		
		//check if any update is available
		obj1.update();
		
		//now send message to subject
		topic.postMessage("New Message");
	}

}

运行程序 结果如下

Obj1:: No new message
Message Posted to Topic:New Message
Obj1:: Consuming message::New Message
Obj2:: Consuming message::New Message
Obj3:: Consuming message::New Message

java 观察者类图

observer-pattern-450x364.png

观察者模式也被称为发布订阅模式,java中的一些实现如下

  • java.util.EventListener in Swing
  • javax.servlet.http.HttpSessionBindingListener
  • javax.servlet.http.HttpSessionAttributeListener

上面就是java中的观察者模式,希望你能喜欢

原文链接