观察者模式

前言

最近在看《深入浅出Nodejs》,看完了第三章异步IO,觉得观察者模式在Node中十分重要于是找了几篇博客学习了一下观察者模式。

主要的参考博客:
《JAVA与模式》之观察者模式——java_my_life
java:从消息机制谈到观察者模式——luoweifu

观察者模式的定义

观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

观察者模式结构

偷懒直接拿了别人的图:

  • Subject 抽象主题,也被叫做被观察者(Observable),抽象主题提供一个接口,可以添加和删除观察者
  • ConcreteSubject 具体主题,也被叫做具体被观察者(Concreate Obervable),存放有关状态,并且在状态改变时通知观察者
  • Observer 抽象观察者,为所有具体观察者定义一个更新接口,用于在得到主题更新时更新自己,并做一些相应操作。
  • ConcreteObserver 具体观察者,存储与主题相关的状态并且实现更新接口,可以持有一个具体主题对象的引用。

推模型和拉模型

观察者模式可以分为拉模型和推模型两种:

  • 推模型 主题对象像观察者推送信息,不管观察者是否需要,推送的信息一般是主题对象的全部或部分数据。
  • 拉模型 主题对象在通知观察者时,只提供少量信息,若观察者需要更多信息,则直接到主题对象中获取数据。这样就需要通过update()方法将主题对象传递给观察者,或者让观察者持有一个具体主题对象的引用。

推模型

//Observer.java
public interface Observer {
	void update(String newState);
}
//Observable.java
public abstract class Observable {
	private ArrayList<Observer> observerList = new ArrayList<>();
	
	public void addObserver(Observer o) {
		observerList.add(o);
	}
	
	public void removeObserver(Observer o) {
		observerList.remove(o);
	}
	
	public void nodifyObservers(String newState) {
		for(Observer observer : observerList) {
			observer.update(newState);
		}
	}
}
//ConcreteObserver.java
public class ConcreteObserver implements Observer {

	public ConcreteObserver(Observable o) {
		o.addObserver(this);
	}
	
	@Override
	public void update(String newState) {
		System.out.println("状态已更新:" + newState);
	}
}
//ConcreteObservable.java
public class ConcreteObservable extends Observable {
	private String state = "";

	public String getState() {
		return state;
	}

	public void setState(String state) {
		if(!this.state.equals(state)) {
			this.state = state;
			this.nodifyObservers(state);
		}
	}
}
//Client.java
public class Client {
	public static void main(String[] args) {
		ConcreteObservable concreteObservable = new ConcreteObservable();
		ConcreteObserver concreteObserver = new ConcreteObserver(concreteObservable);
		concreteObservable.setState("神清气爽");
	}
}

/*OUTPUT:
 状态已更新:神清气爽
 */

拉模型

Java提供的观察者模式应该就是一种拉模型(个人理解),下面的代码删去了一些源码中的注释。

Java源码

//Observer.java
package java.util;
public interface Observer {
    void update(Observable o, Object arg);
}
//Observable.java
package java.util;
public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;
    public Observable() {
        obs = new Vector<>();
    }
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }
    
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }
    
    public void notifyObservers() {
        notifyObservers(null);
    }
    
    public void notifyObservers(Object arg) {
        Object[] arrLocal;

        synchronized (this) {
            /* We don't want the Observer doing callbacks into
             * arbitrary code while holding its own Monitor.
             * The code where we extract each Observable from
             * the Vector and store the state of the Observer
             * needs synchronization, but notifying observers
             * does not (should not).  The worst result of any
             * potential race-condition here is that:
             * 1) a newly-added Observer will miss a
             *   notification in progress
             * 2) a recently unregistered Observer will be
             *   wrongly notified when it doesn't care
             */
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }
    
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }
    
    protected synchronized void setChanged() {
        changed = true;
    }
    
    protected synchronized void clearChanged() {
        changed = false;
    }
    
    public synchronized boolean hasChanged() {
        return changed;
    }
    
    public synchronized int countObservers() {
        return obs.size();
    }
}

需要注意的是notifyObservers()这个方法中,从Vector中取出Observers并放在一个Object数组中,这个操作是在synchronized块中的。但是调用Observer的update()没有在synchronized块中(猜想这个地方是为了防止Observer的update()也需要锁住Observable而造成死锁,也许也有防止被Observa被锁住太长时间的目的),因此会有竞争的风险

使用

Watched.java

public class Watched extends Observable {
	private String data = "";

	public String getData() {
		return data;
	}

	public void setData(String data) {
		if(!this.data.equals(data)){
			this.data = data;
			setChanged();
		}
		this.notifyObservers();
	}
}

Watcher.java

public class Watcher implements Observer{
	
	public Watcher(Observable observable) {
		observable.addObserver(this);
	}

	@Override
	public void update(Observable o, Object arg) {
		System.out.println("状态已改变:" + ((Watched)o).getData());
	}
}

DemoClient.java

public class DemoClient {
	public static void main(String[] args) {
		Watched watched = new Watched();
		Watcher watcher = new Watcher(watched);
		watched.setData("好——像有点不对劲!");
	}
}
/*OUTPUT:
 状态已改变:好——像有点不对劲!
 */
原文地址:https://www.cnblogs.com/FJH1994/p/5865040.html