观察者模式
观察者模式又叫发布-订阅(Publish/Subscribe)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。[DP]
观察者模式(Observer)结构图
Subject类,可翻译为主题或抽象通知者,一般用一个抽象类或者一个接口实现。它把所有对观察者对象的引用保存在一个聚焦里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
public abstract class Subject {
private List observers = new ArrayList<>();
/**
* 增加观察者
* @param observer
*/
public void attach(Observer observer){
observers.add(observer);
}
/**
* 移除观察者
* @param observer
*/
public void detach(Observer observer){
observers.remove(observer);
}
/**
* 通知观察者
*/
public void notifyObs(){
for(Observer observer : observers){
observer.update();
}
}
}
Observer类,抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者一般用一个抽象类或者一个接口实现。更新接口通常包含一个update()方法,这个方法叫做更新方法。
public abstract class Observer {
public abstract void update();
}
ConcreteSubject类,叫做具体主题或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
public class ConcreteSubject extends Subject {
/**
* 具体被观察者状态
*/
private String subjectState;
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
}
}
ConcreteObserver类,具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。具体观察者角色可以保存一个指向主题对象的引用。具体观察者角色通常用一个具体子类实现。
public class ConcreteObserver extends Observer {
private String name;
private String observerState;
private ConcreteSubject concreteSubject;
public ConcreteObserver(ConcreteSubject concreteSubject, String name) {
this.concreteSubject = concreteSubject;
this.name = name;
}
@Override
public void update() {
observerState = concreteSubject.getSubjectState();
print("观察者" + name + "的新状态是" + observerState);
}
}
客户端代码
public class Test {
public static void main(String[] args){
ConcreteSubject concreteSubject = new ConcreteSubject();
concreteSubject.attach(new ConcreteObserver(concreteSubject,"X"));
concreteSubject.attach(new ConcreteObserver(concreteSubject,"Y"));
concreteSubject.attach(new ConcreteObserver(concreteSubject,"Z"));
concreteSubject.setSubjectState("ABC");
concreteSubject.notifyObs();
}
}
结果显示
观察者模式特点
将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便[DP]。
而观察者模式的关键对象时主题Subject和观察者Observer,一个Subject可以有任意数目的依赖它的Observer,一旦Subject的状态发生了改变,所有的Observer都可以得到通知。Subject发出通知时并不需要知道谁是它的观察者,也就是说,具体观察者是谁,它根本不需要知道。而任何一个具体观察者不知道也不需要知道其他观察者的存在。
当一个对象的改变需要同时改变其他对象的时候,而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。
当一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。
观察者模式的现实编程中,具体的观察者完全有可能是风马牛不相及的类,但它们都需要根据通知者的通知来做出update()的操作,所以让它们都实现下面这样一个接口就可以实现这个想法了。
public interface ObserverInterface {
void update();
}
观察者模式的不足
尽管已经用了依赖倒转原则,但是‘抽象通知者’还是依赖‘抽象观察者’,也就是说,万一没有了抽象观察者这样的接口,通知的功能就完成不了。另外就是每个具体观察者,它不一定是‘更新’的方法要调用,比如希望控件的隐藏、打开等,根本就不是同名的方法。
事件委托实现(JAVA可通过反射实现)
情境:公司员工委托前台来通知老板是否回来,员工有的在看股票,有的在看NBA
Event类,定义一个事件类
public class Event {
/**
* 要执行方法的对象
*/
private Object object;
/**
* 要执行的方法名称
*/
private String methodName;
/**
* 要执行方法的参数
*/
private Object[] params;
/**
* 要执行方法的参数类型
*/
private Class[] paramTypes;
public Event() {
}
public Event(Object object, String methodName, Object... args) {
this.object = object;
this.methodName = methodName;
this.params = args;
contractParamTypes(this.params);
}
/**
* 根据参数数组生成参数类型数组
*
* @param params
*/
private void contractParamTypes(Object[] params) {
if (params != null && params.length > 0) {
this.paramTypes = new Class[params.length];
for (int i = 0; i < params.length; i++) {
this.paramTypes[i] = params[i].getClass();
}
}
}
public Object getObject() {
return object;
}
/**
* 执行该 对象的该方法
*
* @throws Exception
*/
public void invoke() throws Exception {
Method method = object.getClass().getMethod(this.getMethodName(), this.getParamTypes());
if (null == method) {
return;
}
method.invoke(this.getObject(), this.getParams());
}
public void setObject(Object object) {
this.object = object;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Object[] getParams() {
return params;
}
public void setParams(Object[] params) {
this.params = params;
}
public Class[] getParamTypes() {
return paramTypes;
}
}
EventHandler类,若干Event类的载体,同时提供一个执行所有Event的方法
public class EventHandler {
private List objects;
public EventHandler(){
objects=new ArrayList<>();
}
/**
* 添加某个对象要执行的事件,及需要的参数
* @param object
* @param methodName
* @param args
*/
public void addEvent(Object object,String methodName,Object...args){
objects.add(new Event(object,methodName,args));
}
/**
* 通知所有的对象执行指定的事件
* @throws Exception
*/
public void notifyX() throws Exception{
for(Event e : objects){
e.invoke();
}
}
}
“看股票观察者”类和“看NBA观察者”类,去掉了父类“抽象观察类”,并将“更新”方法名改为各自适合的方法名。
public class StockObserver {
private String name;
private Subject subject;
public StockObserver(String name,Subject subject){
this.name = name;
this.subject = subject;
}
public void closeStockMarket(){
print(subject.getSubjectState()+" "+name+"关闭股票行情,继续工作");
}
}
public class NBAObserver {
private String name;
private Subject subject;
public NBAObserver(String name, Subject subject){
this.name = name;
this.subject = subject;
}
public void closeNBA(){
print(subject.getSubjectState()+" "+name+"关闭NBA直播,继续工作");
}
}
“抽象通知者”由于不希望依赖“抽象观察者”,所有“增加”和“减少”的方法也就没有必要了(抽象观察者已经不存在了)
public interface Subject {
/**
* 通知观察者
*/
void notifyObs() throws Exception;
String getSubjectState();
}
老板通知者
public class Boss implements Subject {
public EventHandler eventHandler = new EventHandler();
private String state;
public void setSubjectState(String state) {
this.state = state;
}
@Override
public void notifyObs() throws Exception {
this.eventHandler.notifyX();
}
@Override
public String getSubjectState() {
return state;
}
public EventHandler getEventHandler() {
return eventHandler;
}
public void setEventHandler(EventHandler eventHandler) {
this.eventHandler = eventHandler;
}
}
测试代码
public class Test {
public static void main(String[] args) throws Exception {
//创建一个老板通知者
Boss boss = new Boss();
boss.setSubjectState("哈哈哈");
StockObserver stockObserver = new StockObserver("A",boss);
NBAObserver nbaObserver = new NBAObserver("B",boss);
boss.eventHandler.addEvent(stockObserver,"closeStockMarket",null);
boss.eventHandler.addEvent(nbaObserver,"closeNBA",null);
boss.notifyObs();
}
}
运行结果