观察者模式的定义
定义对象间的一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
观察者模式的本质
触发联动。
观察者模式的优缺点
优点
- 观察者模式实现了动态联动(一个操作会引起其他相关的操作)。
- 观察者模式支持广播通信。
缺点
- 可能引起无畏的操作。不管观察者需不需要都会广播。
推模型和拉模型
推模型:目标对象主动向观察者推送内容,属于广播通信。
拉模型:目标对象只传递少量信息。如果需要更具体的信息,由观察者主动到目标对象中获取。
在示例代码2中使用的是拉模型(即直接传入对象,由观察者按需取)。而实现推模型则是直接把内容(这里是content)直接作为参数传过去。
示例1
import java.util.ArrayList;
import java.util.List;
/**
* 目标对象,它知道观察它的观察者,并提供注册和删除观察者的接口
*/
public class Subject {
/**
* 用来保存注册的观察者对象
*/
private List<Observer> observers = new ArrayList<>();
/**
* 注册观察者对象
* @param observer
*/
public void attach(Observer observer) {
observers.add(observer);
}
/**
* 删除观察者对象
*/
public void detach(Observer observer) {
observers.remove(observer);
}
protected void notifyObservers() {
for (Observer observer : observers) {
observer.update(this);
}
}
}
/**
* 具体的目标对象,负责把有关状态存入到相应的观察者对象
* 并在自己状态发生改变时,通知各个观察者
*/
public class ConcreteSubject extends Subject {
/**
* 示意,目标对象的状态
*/
private String subjectState;
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
//状态发生了改变,通知各个观察者。
this.notifyObservers();
}
public String getSubjectState() {
return subjectState;
}
}
/**
* 观察者接口,定义一个更新的接口给那些在目标发生改变的时候被通知的对象
*/
public interface Observer {
/**
* 更新的接口
* @param subject
*/
void update(Subject subject);
}
/**
* 具体观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致
*/
public class ConcreteObserver implements Observer {
/**
* 示意,观察者状态
*/
private String observerState;
@Override
public void update(Subject subject) {
//具体的更新实现
//这里可能需要更新观察者的状态,使其与目标状态一致
observerState = ((ConcreteSubject) subject).getSubjectState();
}
}
示例2
import java.util.ArrayList;
import java.util.List;
/**
* 目标对象,作为被观察者
*/
public class Subject {
/**
* 用来保存注册的观察者对象
*/
private List<Observer> readers = new ArrayList<>();
/**
* 注册订阅者
* @param reader
*/
public void attach(Observer reader) {
readers.add(reader);
}
/**
* 取消订阅
* @param reader
*/
public void detach(Observer reader) {
readers.remove(reader);
}
/**
* 通知读者
*/
protected void notifyObservers() {
for (Observer reader : readers) {
reader.update(this);
}
}
}
/**
* 具体的目标对象,负责把有关状态存入到相应的观察者对象
* 并在自己状态发生改变时,通知各个观察者
*/
public class NewsPaper extends Subject {
/**
* 报纸的具体内容
*/
private String content;
/**
* 获取报纸的具体内容
* @return
*/
public String getContent() {
return content;
}
/**
* 示意,设置报纸的具体内容,相当于出版了
* @param content
*/
public void setContent(String content) {
this.content = content;
//出版了就要通知读者
notifyObservers();
}
}
/**
* 观察者,比如报纸的读者
*/
public interface Observer {
/**
* 被通知的方法
* @param subject 具体的目标对象,可以获取报纸的内容
*/
void update(Subject subject);
}
/**
* 观察者(读者)
*/
public class Reader implements Observer {
private String name;
@Override
public void update(Subject subject) {
String content = ((NewsPaper) subject).getContent();
System.out.println("观察者{"+name+"}收到了内容:{"+content+"}");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class TestClient {
public static void main(String[] args) {
//注册订阅者
NewsPaper newsPaper = new NewsPaper();
Reader reader1 = new Reader();
reader1.setName("张三");
Reader reader2 = new Reader();
reader2.setName("李四");
newsPaper.attach(reader1);
newsPaper.attach(reader2);
//要出报纸了
newsPaper.setContent("研磨设计模式");
}
}
示例3 Java内置
import java.util.Observable;
public class NewsPaper extends Observable {
/**
* 报纸的具体内容
*/
private String content;
/**
* 获取报纸的具体内容
* @return
*/
public String getContent() {
return content;
}
/**
* 示意,设置报纸的具体内容,
* @param content
*/
public void setContent(String content) {
this.content = content;
//用Java的Observable下面这句话必不可少
this.setChanged();
//推模型
this.notifyObservers(this.content);
// //拉模型
// this.notifyObservers();
}
}
import java.util.Observable;
import java.util.Observer;
/**
* 观察者(读者)
*/
public class Reader implements Observer {
private String name;
@Override
public void update(Observable o, Object content) {
// Java中默认的是拉模式,Java会默认传递目标实现对象本身
// 所以不管调用的是哪一个notifyObservers方法,第一个参数总是有值。
// 但第二个参数必须是使用有参的notifyObservers才能有值。
//推模式
System.out.println("推模式:观察者{"+name+"}收到了内容:{"+content+"}");
//拉模式
String contentPull = ((NewsPaper) o).getContent();
System.out.println("拉模式:观察者{"+name+"}收到了内容:{"+contentPull+"}");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class TestClient {
public static void main(String[] args) {
//注册订阅者
NewsPaper newsPaper = new NewsPaper();
Reader reader1 = new Reader();
reader1.setName("张三");
Reader reader2 = new Reader();
reader2.setName("李四");
newsPaper.addObserver(reader1);
newsPaper.addObserver(reader2);
//要出报纸了
newsPaper.setContent("研磨设计模式");
}
}