3.1 抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
3.2 具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
3.3 抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
3.4 具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
4、类图
5. 模式总结
5.1 优点
5.1.1 观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。
5.2 缺点
5.2.1 依赖关系并未完全解除,抽象通知者依旧依赖抽象的观察者。
5.3 适用场景
5.3.1 当一个对象的改变需要给变其它对象时,而且它不知道具体有多少个对象有待改变时。
5.3.2 一个抽象某型有两个方面,当其中一个方面依赖于另一个方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
6、java代码实现
主题抽象类:
import java.util.ArrayList;
import java.util.List;
abstract public class Subject {
private List<Listener> listenerList=new ArrayList<Listener>();
public void addListener(Listener listener){
for(Listener curlistener:listenerList){
if(curlistener==listener){
return;
}
}
listenerList.add(listener);
}
public void deleteListener(Listener listener){
// 删除的时候,注意倒序遍历List
for(int i=listenerList.size()-1;i>=0;i--){
Listener curlistener=listenerList.get(i);
if(curlistener==listener){
listenerList.remove(i);
}
}
}
public void notifyAllListeners() {
for(final Listener listener:listenerList){
/*此处用线程更好,因为如果其中一个观察者的update异常了或者执行时间太长,
就会影响到其他观察者的update方法,用线程就避免了这个问题。*/
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
listener.update();
}
});
thread.start();
}
}
}
主题实现类:
public class Scretary extends Subject {
private boolean isback=false;
public void changeBossState(){
this.isback=true;
System.out.println("老板回来了,大家快注意!");
this.notifyAllListeners();
}
}
监听者接口类
public interface Listener {
public void update();
}
监听者实现类
public class ChatListener implements Listener {
@Override
public void update() {
System.out.println("停止聊天,开始工作");
}
}
public class StockListener implements Listener {
@Override
public void update() {
System.out.println("关闭股票窗口,开始工作");
}
}
client端代码:
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
Listener stockListener=new StockListener();
Listener chatListener=new ChatListener();
Scretary scretary=new Scretary();
scretary.addListener(stockListener);
scretary.addListener(chatListener);
scretary.changeBossState();
}
}
测试结果:
7、注意事项:
7.1抽象观察者一般用接口实现,因为监听的类你不知道它是什么类型,不知道这个类是不是已经继承过其他父类,所以用接口实现更好。
7.2代码中我把Observer都改成了Listener,因为觉得方法名attach、dettach都不太容易让人理解是什么意思,改成addListener,deleteLinstener更容易理解,举例一个输入框分别有两类监听者,单击有一群监听者,双击也有一群监听者,名字就可以改成addClickListener、addDoubleClickListener,比attach容易扩展。
7.3通知各个监听者的时候用线程更好,因为如果其中一个观察者的update异常了或者执行时间太长, 就会影响到其他观察者的update方法,用线程就避免了这个问题。