观察者模式
观察者模式(有时又被称为发布(publish )-订阅(Subscribe)模式、模型-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
观察者模式中的角色
观察者(Observer): 为那些在目标发生改变时需获得通知的对象定义一个更新接口。当它的状态发生改变时, 向它的各个观察者发出通知。
具体观察者(ConcreteObserver): 维护一个指向ConcreteSubject对象的引用。存储有关状态,这些状态应与目标的状态保持一致。实现Observer的更新接口以使自身状态与目标的状态保持一致。
主题(Subject): 主题知道它的观察者。可以有任意多个观察者观察同一个主题。 提供注册和删除观察者对象的接口。
具体的主题(ConcreteSubject): 将有关状态存入各ConcreteObserver对象。
观察者模式的应用场景
1、 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
2、 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。
观察者模式具体实现:
观察者接口:
Observer.java
package com.lingdu.observer;
/**
* 观察者模式
* @author LingDu
*/
public interface Observer {
/**
* 更新的方法
* @param msg 返回一个消息
*/
public void update(String msg);
}
具体的观察者
ImageMessageObserver.java
- 用于处理图片消息的观察者
package com.lingdu.observer;
/**
* 具体的观察者实现类
* 处理图片消息的观察者
* @author LingDu
*/
public class ImageMessageObserver implements Observer {
@Override
public void update(String msg) {
//如果开头的字符串是以image开头,才进行处理。
if(msg.startsWith("image")){
System.out.println("接收图片消息");
}
}
}
TextMessageObserver.java
- 用于处理文本消息的观察者
package com.lingdu.observer;
/**
* 具体的观察者实现类
* 处理文本消息的观察者
* @author LingDu
*/
public class TextMessageObserver implements Observer{
@Override
public void update(String msg) {
//如果开头的字符串是以text开头,才进行处理。
if(msg.startsWith("text")){
System.out.println("接收文本消息。");
}
}
}
主题接口:
Subject.java
package com.lingdu.observer;
import java.util.ArrayList;
import java.util.List;
/**
* 主题接口
* @author LingDu
*/
public interface Subject {
/**
* 观察者集合
*/
List<Observer> observerList = new ArrayList<Observer>();
/**
* 添加观察者
* @param obs
*/
public void addObserver(Observer obs);
/**
* 移除观察者
* @param obs
*/
public void removeBoserver(Observer obs);
/**
* 通知
* @param msg
*/
public void notify(String msg);
}
具体的主题
ConcreteSubject.java
package com.lingdu.observer;
/**
* 具体的主题
* @author LingDu
*/
public class ConcreteSubject implements Subject {
/**
* 添加观察者
* @param obs 观察者对象
*/
@Override
public void addObserver(Observer obs) {
Subject.observerList.add(obs);
}
/**
* 移除观察者
* @param obs 观察者对象
*/
@Override
public void removeBoserver(Observer obs) {
Subject.observerList.remove(obs);
}
/**
* 调用相应对象的通知
* @param msg 传入的消息
*/
@Override
public void notify(String msg) {
for (Observer observer : observerList) {
observer.update(msg);
}
}
}
测试
Test.java
package com.lingdu.observer;
public class Test {
public static void main(String[] args){
//创建具体的主题对象
ConcreteSubject concreteSubject = new ConcreteSubject();
//创建文本观察者对象
TextMessageObserver text = new TextMessageObserver();
//创建图片观察者对象
ImageMessageObserver image = new ImageMessageObserver();
//添加观察者
concreteSubject.addObserver(text);
concreteSubject.addObserver(image);
//传入文本消息和图片消息进行测试
concreteSubject.notify("text:");
concreteSubject.notify("image");
//将文本观察者移除
concreteSubject.removeBoserver(text);
//再次传入文本消息进行测试
concreteSubject.notify("text:因为已经被移除了,所以我不会被输出");
}
}
观察者模式的优缺点
优点:
1、 Subject和Observer之间是松偶合的,分别可以各自独立改变。
2、 Subject在发送广播通知的时候,无须指定具体的Observer,Observer可以自己决定是否要订阅Subject的通知。
3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。
缺点:
1、 松偶合导致代码关系不明显,有时可能难以理解。
2、 如果一个Subject被大量Observer订阅的话,在广播通知的时候可能会有效率问题。(毕竟只是简单的遍历): 维护一个指向ConcreteSubject对象的引用。存储有关状态,这些状态应与目标的状态保持一致。实现Observer的更新接口以使自身状态与目标的状态保持一致。