观察者模式
定义
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
解决
一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不变。观察者模式的工作实际上就是解耦。
优点
- 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
- 观察者模式在观察目标和观察者之间建立一个抽象的耦合。
- 观察者模式支持广播通信。
- 观察者模式符合“开闭原则”的要求。
缺点
- 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
结构
观察者模式包含如下角色:
- Subject: 主题
- ConcreteSubject: 具体主题
- Observer: 观察者
- ConcreteObserver: 具体观察者
实现
package observermethod;
import java.util.ArrayList;
import java.util.List;
/**
* 抽象通知者(主题Subject),一般用一个抽象类或者一个接口实现。
* 它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以由任何数量的观察者。
* 抽象主题提供一个接口,可以增加和删除观察者对象。
*/
public abstract class Subject {
private List<Observer> observers = new ArrayList<Observer>();
//增加观察者
public void attach(Observer observer) {
observers.add(observer);
}
//移除观察者
public void detach(Observer observer) {
observers.remove(observer);
}
//通知
public void notifyObserver() {
for (Observer observer : observers) {
observer.update();
}
}
}
package observermethod;
/**
* 具体主题或具体通知者,将有关状态存入具体观察者对象;在具体主题的
* 内部状态改变时,给所有登记过的观察者发出通知。
* 具体主题角色通常用一个具体子类实现
*/
public class ConcreteSubject extends Subject{
private String subjectState;
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
}
}
package observermethod;
/**
* 抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时
* 更新自己。这个接口叫做更新接口。抽象观察者一般用一个抽象类或者
* 一个接口实现。更新接口通常包括Update()方法,这个方法叫做更新方法。
*/
public abstract class Observer {
//更新方法
public abstract void update();
}
package observermethod;
public class ObserverClient {
public static void main(String[] args) {
//创建Subject;
ConcreteSubject s=new ConcreteSubject();
//添加观察者X,Y,Z: 一对三
s.attach(new ConcreteObserver(s,"X"));
s.attach(new ConcreteObserver(s,"Y"));
s.attach(new ConcreteObserver(s,"Z"));
//设置主题状态
s.setSubjectState("ABC");
//更新
s.notifyObserver();
}
}
实例
老板回办公室,秘书提前看到会跟摸鱼的同时打电话通知。但有时秘书并没有功夫打电话通知。这里的通知者、主题(Subject) 有老板和秘书两个,观察者有看股票的看球的。
package observermethod.demo;
/**
* 通知者接口
*/
interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyObserver();
String getSubjectState();
void setSubjectState(String value);
}
package observermethod.demo;
import java.util.ArrayList;
import java.util.List;
public class Secretary implements Subject{
private List<Observer> observers = new ArrayList<Observer>();
private String state;
@Override
public void attach(Observer observer) {
observers.add(observer);
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObserver() {
for (Observer observer : observers) {
observer.update();
}
}
@Override
public String getSubjectState() {
return state;
}
@Override
public void setSubjectState(String value) {
this.state=value;
}
}
package observermethod.demo;
import java.util.ArrayList;
import java.util.List;
public class Boss implements Subject{
private List<Observer> observers = new ArrayList<Observer>();
private String state;
@Override
public void attach(Observer observer)
{
observers.add(observer);
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObserver() {
for (Observer observer : observers) {
observer.update();
}
}
@Override
public String getSubjectState() {
return state;
}
@Override
public void setSubjectState(String value) {
this.state = value;
}
}
package observermethod.demo;
public abstract class Observer {
protected String name;
protected Subject sub;
public Observer(String name,Subject sub)
{
this.name=name;
this.sub=sub;
}
public abstract void update();
}
package observermethod.demo;
public class BallObserver extends Observer{
public BallObserver(String name, Subject sub) {
super(name, sub);
}
@Override
public void update() {
System.out.println(sub.getSubjectState()+name+"关闭球赛网页,继续工作!");
}
}
package observermethod.demo;
public class StockObserver extends Observer{
public StockObserver(String name, Subject sub) {
super(name, sub);
}
@Override
public void update() {
System.out.println(sub.getSubjectState()+name+"关闭股票行情,继续工作!");
}
}
package observermethod.demo;
/**
* 老板回办公室,秘书提前看到会跟摸鱼的同时打电话通知。
* 但有时秘书并没有功夫打电话通知。
*
* 这里的通知者、主题(Subject) 有老板和秘书两个
* 观察者有看股票的看球的。
*/
public class ObserverClient {
public static void main(String[] args) {
/**
* 老板回来了,秘书提前通知同事
*/
{
System.out.println("情景:老板回来,秘书打电话通知。");
//秘书:小秘
Secretary xiaomi =new Secretary();
//看股票的人:阿股
StockObserver agu =new StockObserver("阿股",xiaomi);
//看球赛的人:球球
BallObserver qiuqiu=new BallObserver("球球",xiaomi);
//通知
xiaomi.attach(agu);
xiaomi.attach(qiuqiu);
//老板胡汉三回来了,设置其状态
xiaomi.setSubjectState("秘书看见老板回来,打电话通知。");
//发出通知-->然后各个观察者的就要update了
xiaomi.notifyObserver();
}
System.out.println();
/**
* 这里老板径直回来了,秘书来不及通知
*/
{
System.out.println("情景:老板回来了。");
//老板:胡汉三
Boss huhansan =new Boss();
//看股票的人:阿股
StockObserver agu =new StockObserver("阿股",huhansan);
//看球赛的人:球球
BallObserver qiuqiu=new BallObserver("球球",huhansan);
//通知
huhansan.attach(agu);
huhansan.attach(qiuqiu);
//阿股没看见老板(没通知到),把他减去
huhansan.detach(agu);
//老板胡汉三回来了,设置其状态
huhansan.setSubjectState("我胡汉三回来了!");
//发出通知-->然后各个观察者的就要update了
huhansan.notifyObserver();
}
}
}
总结
- 观察者模式定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅模式、模型-视图模式、源-监听器模式或从属者模式。观察者模式是一种对象行为型模式。
- 观察者模式包含四个角色:目标又称为主题,它是指被观察的对象;具体目标是目标类的子类,通常它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;观察者将对观察目标的改变做出反应;在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致。
- 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。
- 观察者模式的主要优点在于可以实现表示层和数据逻辑层的分离,并在观察目标和观察者之间建立一个抽象的耦合,支持广播通信;其主要缺点在于如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间,而且如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 观察者模式适用情况包括:一个抽象模型有两个方面,其中一个方面依赖于另一个方面;一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变;一个对象必须通知其他对象,而并不知道这些对象是谁;需要在系统中创建一个触发链。
- 在JDK的java.util包中,提供了Observable类以及Observer接口,它们构成了Java语言对观察者模式的支持