Java 设计模式之观察者模式的详解(行为模式)

观察者(Observer)模式:是对象的行为模式,又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听(Source/Listener)模式或者从属(Dependents)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

观察者模式类的结构:


观察者模式角色如下:

抽象主题(Subject)角色:抽象主题角色提供维护一个观察者对象聚集的操作方法,对聚集的增加、删除等。

具体主题(ConcreteSubject)角色:将有关状态存入具体的观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色负责实现抽象主题中聚集的管理方法。

抽象观察者(Observer)角色:为具体观察者提供一个更新接口。

具体观察者(ConcreteObserver)角色:存储与主题相关的自洽状态,实现抽象观察者提供的更新接口。

仔细观察上面的类图,发现具体主题角色和抽象观察者之间的连线,是因为具体主题角色维护了一个观察者引用的聚集,如果有多个具体主题角色,意味着每个具体角色都要维护一个观察者的聚集,那么能不能将聚集提升到抽象主题里面呢?这个就需要考虑场景,如果多个主题实现在管理上都有很大差异,那么就不能提升到抽象角色中,但是绝大多数情况下,这些聚集管理方法本身就是所有具体主题所共有的,所以大多数情况下都是可以将聚集和聚集的管理都移入到抽象主题中的,因为notifyObserver()方法是依赖于聚集的,所以将notifyObserver()也移入抽象主题中,这样就形成了如下的另一种观察者模式结构:


我们这里简单用代码描述如下:

抽象观察者:

public interface Observer {

	public abstract  void update();
}
具体观察者:

public class ConcreteObserver implements Observer{

	@Override
	public void update() {
		// TODO Auto-generated method stub
		//这里写业务逻辑
	}

}
抽象主题:

public interface Subject {

	public void attach(Observer observer);

	public void detach(Observer observer);

	void notifyObservers();
}
具体主题:

public class ConcreteSubject implements Subject {

	private Vector<Observer> observersVector = new Vector<Observer>();

	@Override
	public void attach(Observer observer) {
		// TODO Auto-generated method stub
		observersVector.addElement(observer);
	}

	@Override
	public void detach(Observer observer) {
		// TODO Auto-generated method stub
		observersVector.removeElement(observer);
	}

	@Override
	public void notifyObservers() {
		// TODO Auto-generated method stub
		Enumeration<Observer> enumeration = observers();
		while (enumeration.hasMoreElements()) {
			((Observer) enumeration.nextElement()).update();
		}
	}

	@SuppressWarnings("unchecked")
	public Enumeration<Observer> observers() {
		return ((Vector<Observer>) observersVector.clone()).elements();
	}

}

上面代码描述第一种形式,第二种可以自己实现,这里不再赘述。

接下来我们看java语言是如何支持观察者模式的,java提供一个被观察者类java.util.Observable和一个观察者接口java.util.Observer

jdk1.6API文档如下描述:

public interface Observer

一个可在观察者要得到 observable 对象更改通知时可实现 Observer 接口的类。

从以下版本开始:

JDK1.0

另请参见:

Observable

 

public class Observableextends Object

此类表示模型视图范例中的 observable 对象,或者说数据。可将其子类化,表示应用程序想要观察的对象。

一个 observable 对象可以有一个或多个观察者。观察者可以是实现了 Observer 接口的任意对象。一个 observable 实例改变后,调用 Observable notifyObservers 方法的应用程序会通过调用观察者的 update 方法来通知观察者该实例发生了改变。

 

未指定发送通知的顺序。Observable 类中所提供的默认实现将按照其注册的重要性顺序来通知 Observers,但是子类可能改变此顺序,从而使用非固定顺序在单独的线程上发送通知,或者也可能保证其子类遵从其所选择的顺序。

 

注意,此通知机制与线程无关,并且与 Object 类的 wait notify 机制完全独立。 新创建一个 observable 对象时,其观察者集是空的。当且仅当 equals 方法为两个观察者返回 true 时,才认为它们是相同的。

 

从以下版本开始:

JDK1.0

另请参见:

notifyObservers(), notifyObservers(java.lang.Object), Observer, Observer.update(java.util.Observable, java.lang.Object)


现在 举个例子,如果你看过 TVB 的警匪片,你就知道卧底的工作方式。一般一个警察可能有几个卧底,潜入敌人内部,打探消息,卧底完全靠他的领导的指示干活,领导说几点行动,他必须按照这个时间去执行,如果行动时间改变,他也要立马改变自己配合行动的时间。领导派两个卧底去打入敌人内部,那么领导相当于抽象主题,而督察警官张三这个人派了两个卧底李四和万王五,张三就相当于具体主题,卧底相当于抽象观察者,这两名卧底是李四和王五就是具体观察者,派的这个动作相当于观察者在主题的登记。那么这个类图如下:

利用javaAPI来实现,代码描述如下:

警察张三:

import java.util.List;
import java.util.Observable;
import java.util.Observer;

public class Police extends Observable {

	private String time;

	public Police(List<Observer> list) {
		super();
		for (Observer o : list) {
			addObserver(o);
		}
	}

	public void change(String time) {
		this.time = time;
		setChanged();
		notifyObservers(this.time);
	}
}
卧底A:

import java.util.Observable;
import java.util.Observer;

public class UndercoverA implements Observer{

	private String time; 
	@Override
	public void update(Observable o, Object arg) {
		// TODO Auto-generated method stub
		time = (String) arg;   
        System.out.println("卧底A接到消息,行动时间为:"+time);  
	}
}
卧底B:

import java.util.Observable;
import java.util.Observer;

public class UndercoverB implements Observer{

	String time;
	@Override
	public void update(Observable o, Object arg) {
		// TODO Auto-generated method stub
		time = (String) arg;   
        System.out.println("卧底B接到消息,行动时间为:"+time);  
	}

}
测试:

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

public class Test {

	public static void main(String[] args) {
		UndercoverA o1 = new UndercoverA();
		UndercoverB o2 = new UndercoverB();
		List<Observer> list = new ArrayList<>();
		list.add(o1);
		list.add(o2);
		Police subject = new Police(list);
		subject.change("02:25");
		System.out.println("===========由于消息败露,行动时间提前=========");
		subject.change("01:05");

	}
}

测试运行结果:

卧底B接到消息,行动时间为:02:25

卧底A接到消息,行动时间为:02:25

===========由于消息败露,行动时间提前=========

卧底B接到消息,行动时间为:01:05

卧底A接到消息,行动时间为:01:05

观察者模式的优点是只要订阅/登记了之后,当被观察者改变时,观察者能自动更新。跟JMS一样,消息发布者发出消息时,只要注册过的都会收到消息。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值