源码地址 | https://github.com/DingMouRen/DesignPattern |
---|
- Subject(目标,也就是Observable可观察者)可以有任意多的观察者观察它,同时提供注册和删除观察者对象的接口
- Observer(观察者)定义一个更新接口
- ConcreteSubject(具体目标)将有关状态存入各ConcreteObserver对象,当它状态发生变化时,向它的各个具体观察者发出通知。
- ConcreteObserver(具体观察者)维护这一个指向ConcreteSubject的引用;存储着有关 状态,这些状态与具体目标的状态保持一致;实现了Observer的更新接口来使自身状态与具体目标的状态保持一致。
定义
观察者模式定义了对象间一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。
使用场景
- 当对一个对象的改变需要同时改变其他对象,而又不知道具体有多少对象有待改变时
- 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之 , 你不希望这些对象是紧密耦合的
协作
- 当ConcreteSubject发生任何可能导致其观察者与其本身状态不一致的改变时,它将通知它的各个观察者。
- 在得到一个具体目标的改变通知后,ConcreteObserver对象可向目标对象查询信息。ConcreteObserver使用这些信息来使它的状态与目标对象的状态一致。
举个例子
我们有一个对象ConcreteSubject,可以获取到环境中的温度、湿度和压力,也就是被观察者(目标),这里有两个对象ConcreteObserver系列,用途是显示环境中的温度、湿度和压力,我们要求在ConcreteSubject的数据发生变化时,ConcreteObserver系列的要同时更新数据。
//目标接口:1.定义注册接口 2.定义注销接口 3.定义发送通知的接口
public interface Subject {
void registerObserver(Observer observer);//注册观察者
void removeObserver(Observer observer);//注销观察者
void notifyObservers();//目标状态变化时,调用这个方法通知所有观察者
void notifyObservers(Object arg);//也可以通知所有的观察者,另外可以携带其他的数据
}
//具体目标:1.存储最新的状态数据 2.维护着所有观察者的集合 3.一旦数据更新,向所有观察者发送通知
public class ConcreteSubject implements Subject {
private ArrayList<Observer> observers;//存储注册的观察者对象,ArrayList是线程不安全的,Vector是线程安全的
private float temperature;//温度
private float humidity;//湿度
private float pressure;//压力
public ConcreteSubject() {
this.observers = new ArrayList();
}
@Override
public void registerObserver(Observer observer) {
if (observer == null) throw new NullPointerException();
if (!observers.contains(observer)) observers.add(observer);//判断是否有这个观察者,不然会被通知两回
}
@Override
public void removeObserver(Observer observer) {
int index = observers.indexOf(observer);
if (index >= 0) observers.remove(index);
}
@Override
public void notifyObservers() {
notifyObservers(null);
}
@Override
public void notifyObservers(Object arg) {
for(Observer observer : observers){
observer.update(this,arg);
}
}
public void setNewData(float temperature,float humidity,float pressure){
this.temperature =temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
private void measurementsChanged(){
notifyObservers();
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
//观察者接口
public interface Observer {
void update(Subject subject,Object arg);
}
//用来显示最新的数据的接口
public interface DisplayNewData {
void display();
}
//具体观察者:1.维护着一个目标的引用 2.实现观察者接口,更新状态与目标状态一致
public class ConcreteObserver_1 implements Observer,DisplayNewData {
private Subject subject;
private float temperature;//温度
private float humidity;//湿度
private float pressure;//压力
public ConcreteObserver_1(Subject subject) {//传入目标subject用来注册观察者,自然也可以删除观察者
this.subject = subject;
this.subject.registerObserver(this);
}
/**
* 这里获取的更新数据的方式是“拉”,我们想要什么数据,就通过get方法获得,而不是目标把数据主动推给观察者的
* @param subject
* @param arg
*/
@Override
public void update(Subject subject, Object arg) {
ConcreteSubject concreteSubject = (ConcreteSubject) subject;
this.temperature = concreteSubject.getTemperature();
this.humidity = concreteSubject.getHumidity();
this.pressure = concreteSubject.getPressure();
display();
}
@Override
public void display() {
System.out.println(this.getClass().getSimpleName()+"最新的数据:\n温度:"+temperature+"℃ 湿度:"+humidity+"% 压力:"+pressure);
}
}
使用
public static void main(String[] args) {
//创建目标,可观察者
ConcreteSubject concreteSubject = new ConcreteSubject();
//创建观察者并注册
ConcreteObserver_1 observer_1 = new ConcreteObserver_1(concreteSubject);
ConcreteObserver_2 observer_2 = new ConcreteObserver_2(concreteSubject);
//更新数据
concreteSubject.setNewData(1,2,3);
}
java中的java.util.Observable的缺点:
- 我们想同时具有Observable类和另一个超类的行为时,就会陷入困难,java是不支持多重继承的,限制了Observable的复用潜力
- Observable中的setChanged()被protected修饰的,只能通过继承,违反OO设计原则:多用组合,少用继承
总结
优点
- 观察者与被观者之间是抽象耦合,可以应对业务变化
- 增强系统灵活性,更易扩展
缺点 - 一个观察者卡顿,会影响整体的执行效率,在这种情况下,可以采用异步的方式。
网友评论