观察者模式是对象的行为模式,又叫发布-订阅模式、模型-视图模式、源-监听模式、或从属者模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有的观察者对象,使它们能够自己更新自己。
一个软件系统常常要求在某一个对象的状态发生变化的时候,某些其他的对象做出相应的改变。能做到这一点的设计方案很多,但是为了使系统易于复用,应该选用低耦合度的设计方案。减少对象之间的耦合有助于系统的复用,但是同时设计师需要使这些低耦合度的对象之间能够维持行为的协调一致,保证高度的协作。观测者模式是满足这一要求的各种设计方案中最重要的一种。
观察者模式的结构
下图是一个简单的示意性实现类图:

观察者模式的静态结构可以从类图中看出,在这个观察者模式的实现中,有以下角色:
(1)抽象主题(Subject)角色:主题角色把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,主题角色又叫抽象被观察者(Observable)角色,一般用一个抽象类或者一个接口实现。
(2)抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者角色一般用一个抽象类或者一个接口实现。
(3)具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者角色(Concrete Observer)。具体主题角色通常用一个具体子类实现。
(4)具体观察者(ConcreteObserver)角色:存储于主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体观察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。
在类图中,从具体主题角色指向抽象观察者角色的合成关系,代表具体主题对象可以包含任意多个对抽象观察者对象的引用。由于java的抽象类是不可能有实例的,因此这些引用的的真实类型必定是ConcreteObserver类型,而这些引用的静态类型是Observer类型,这意味着主题对象不需要知道引用了哪些ConcreteObserver类型,而只知道抽象Observer类型,这就使得具体主题对象可以动态的维护一系列的对观察者对象的引用,并且在需要的时候调用每一个观察者的公有的update()方法。
下面给出示意性代码:
1.ject.java
package com.pattem.behavioral.Observer;
/**
* 抽象主题角色
* */
public interface Subject {
//登记一个新的观察者对象
public void attach(Observer observer);
//删除一个已经登记过的观察者对象
public void detach(Observer observer);
//通知所有登记过的观察者对象
public void notifyObserver();
}
2.ConcreteSubject.java
package com.pattem.behavioral.Observer;
import java.util.Enumeration;
import java.util.Vector;
import javax.sound.sampled.EnumControl;
public class ConcreteSubject implements Subject{
private Vector<Observer> observersVector = new Vector<Observer>();
public void attach(Observer observer) {
observersVector.addElement(observer);
}
public void detach(Observer observer) {
observersVector.removeElement(observer);
}
public void notifyObserver() {
Enumeration enumeration = observers();
while(enumeration.hasMoreElements()){
((Observer)enumeration.nextElement()).update();
}
}
public Enumeration observers(){
return ((Vector)observersVector.clone()).elements();
}
}
3.Observer.java
package com.pattem.behavioral.Observer;
/**
* 抽象观察者角色
* */
public interface Observer {
//调用这个方法更新自己
void update();
}
4.ConcreteObserver.java
package com.pattem.behavioral.Observer;
/**
* 具体观察者角色
* */
public class ConcreteObserver implements Observer{
public void update() {
//修改方法
System.out.println("I am notified");
}
}
如果仔细考察主题功能的对象的功能时,可以发现它必须使用一个java集合来维护一个对所有的观察者对象的引用。而在前面所给出的实现里面,管理这个集合的方法是由抽象主题角色声明并由具体主题角色实现的。这才导致了类图中从具体主题角色到抽象观察者角色的连线。
但是一个自然的问题就是,这些集合管理方法难道在每一个具体主题角色中都不同吗?答案是否定的。在大多数情况下,这些集合管理方法本身就是所有具体主题角色所共有的,因此这些方法连同集合本身都可以移动到抽象主题角色中去。同意由于notifyObserver()方法依赖集合对象,也可以移到抽象主题角色中去。 这就是第二种实现方案。其结构类图如下:

下面给出抽象主题的源码:
Subject.java
package com.pattem.behavioral.Observer2;
import java.util.Enumeration;
import java.util.Vector;
/**
* 抽象主题角色
* */
public abstract class Subject {
//保存对观察者对象的引用
private Vector<Object> observerVector = new Vector<Object>();
//登记一个新的观察者对象
public void attach(Observer observer){
observerVector.addElement(observer);
System.out.println("Attach an observer");
}
//删除一个已经登记过的观察者对象
public void detach(Observer observer){
observerVector.removeElement(observer);
}
public void notifyObserver(){
Enumeration<Object> enumeration = observers();
while(enumeration.hasMoreElements()){
((Observer)enumeration.nextElement()).update();
}
}
//给出观察者聚集的 Enumeration对象
public Enumeration<Object> observers(){
return ((Vector)observerVector.clone()).elements();
}
}
ConcreteSubject.java
package com.pattem.behavioral.Observer2;
/**
* 具体主题角色
* */
public class ConcreteSubject extends Subject {
private String state;
public void change(String state){
this.state = state;
this.notifyObserver();
}
}
Client.java
package com.pattem.behavioral.Observer2;
/**
* 客户端代码 Client
* */
public class Client {
private static ConcreteSubject subject;
private static Observer observer;
public static void main(String[] args) {
//创建主题对象
subject = new ConcreteSubject();
//创建观察者对象
observer = new ConcreteObserver();
//将观察者对象登记到
subject.attach(observer);
//改变主题对象的状态
subject.change("new state");
}
}
在运行时,这个客户端首先创建了具体主题类的实例,以及一个观察对象,然后它调用主题对象的attach()方法,将这个观察者对象向主题对象登记,也就是将它加入到主题对象的集合中去。
java语言提供对观察者模式的支持
在java语言的java.util库里面,提供了一个Observable的类和一个Observer的接口,构成java语言对观察者模式的支持。(详情,请参考java.util.Observable 类源码)
本文深入解析观察者模式的定义、结构和实现,包括简单示意性代码演示及Java语言下对该模式的支持。通过具体示例展示了如何在Java中应用观察者模式,以及其在软件设计中的重要性。

4813

被折叠的 条评论
为什么被折叠?



