@pastqing
2015-10-19T07:58:08.000000Z
字数 7065
阅读 3078
java 设计模式
观察者模式又称为订阅—发布模式,在此模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来事件处理系统。
首先看下观察者模式的类图描述:
观察者模式的角色如下:
从这个类图可以看出, 主题类中维护了一个实现观察者接口的类列表, 主题类通过这个列表来对观察者进行一系列的增删改操作。观察者类也可以主动调用update方法来了解获取主题类的状态更新信息。
以上的类图所描述的只是基本的观察者模式的思想, 有很多不足。比如作为观察者也可以主动订阅某类主题等。下面的例子将进行一些改动, 以便适用具体的业务逻辑。
我们构建一个观察者和主题类, 观察者可以主动订阅主题或者取消主题。主题类统一被一个主题管理者所管理。下面给出类图:

public interface Subject {//注册一个observerpublic void register(Observer observer);//移除一个observerpublic void remove(Observer observer);//通知所有观察者public void notifyObservers();//获取主题类要发布的消息public String getMessage();}
public class MySubject implements Subject {private List<Observer> observers;private boolean changed;private String message;//对象锁, 用于同步更新观察者列表private final Object mutex = new Object();public MySubject() {observers = new ArrayList<Observer>();changed = false;}@Overridepublic void register(Observer observer) {if (observer == null)throw new NullPointerException();//保证不重复if (!observers.contains(observer))observers.add(observer);}@Overridepublic void remove(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {// temp listList<Observer> tempObservers = null;synchronized (mutex) {if (!changed)return;tempObservers = new ArrayList<>(this.observers);this.changed = false;}for(Observer obj : tempObservers) {obj.update();}}//主题类发布新消息public void makeChanged(String message) {System.out.println("The Subject make a change: " + message);this.message = message;this.changed = true;notifyObservers();}@Overridepublic String getMessage() {return this.message;}}
ConcerteSubject做出更新时, 就通知列表中的所有观察者, 并且调用观察者update方法以实现接受通知后的逻辑。这里注意notifyObservers中的同步块。在多线程的情况下, 为了避免主题类发布通知时, 其他线程对观察者列表的增删操作, 同步块中用一个临时List来获取当前的观察者列表。
public class SubjectManagement {//一个记录 名字——主题类 的Mapprivate Map<String, Subject> subjectList = new HashMap<String, Subject>();public void addSubject(String name, Subject subject) {subjectList.put(name, subject);}public void addSubject(Subject subject) {subjectList.put(subject.getClass().getName(), subject);}public Subject getSubject(String subjectName) {return subjectList.get(subjectName);}public void removeSubject(String name, Subject subject) {}public void removeSubject(Subject subject) {}//singletonprivate SubjectManagement() {}public static SubjectManagement getInstance() {return SubjectManagementInstance.instance;}private static class SubjectManagementInstance {static final SubjectManagement instance = new SubjectManagement();}}
主题类管理器的作用就是在观察者订阅某个主题时, 获取此主题的实例对象。
public interface Observer {public void update();public void setSubject(Subject subject);}
public class MyObserver implements Observer {private Subject subject;// get the notify message from Concentrate Subject@Overridepublic void update() {String message = subject.getMessage();System.out.println("From Subject " + subject.getClass().getName()+ " message: " + message);}@Overridepublic void setSubject(Subject subject) {this.subject = subject;}// subcirbe some Subjectpublic void subscribe(String subjectName) {SubjectManagement.getInstance().getSubject(subjectName).register(this);}// cancel subcribepublic void cancelSubcribe(String subjectName) {SubjectManagement.getInstance().getSubject(subjectName).remove(this);}}
public class ObserverTest {private static MySubject writer;@BeforeClasspublic static void setUpBeforeClass() throws Exception {writer = new MySubject();//添加一个名为Linus的作家SubjectManagement.getInstance().addSubject("Linus",writer);}@Testpublic void test() {//定义几个读者MyObserver reader1 = new MyObserver();MyObserver reader2 = new MyObserver();MyObserver reader3 = new MyObserver();reader1.setSubject(writer);reader2.setSubject(writer);reader3.setSubject(writer);reader1.subscribe("Linus");reader2.subscribe("Linus");reader3.subscribe("Linus");writer.makeChanged("I have a new Changed");reader1.update();}}
以上就是观察者模式的小示例。可以看出每个主题类都要维护一个相应的观察者列表, 这里可以根据具体主题的抽象层次进一步抽象, 将这种聚集放到一个抽象类中去实现, 来共同维护一个列表, 当然具体操作要看实际的业务逻辑。
再说Servlet中的Listener之前, 先说说观察者模式的另一种形态——事件驱动模型。与上面提到的观察者模式的主题角色一样, 事件驱动模型包括事件源, 具体事件, 监听器, 具体监听器。
Servlet中的Listener就是典型的事件驱动模型。
JDK中有一套事件驱动的类, 包括一个统一的监听器接口和一个统一的事件源, 源码如下:
/*** A tagging interface that all event listener interfaces must extend.* @since JDK1.1*/public interface EventListener {}
这是一个标志接口, JDK规定所有监听器必须继承这个接口。
public class EventObject implements java.io.Serializable {private static final long serialVersionUID = 5516075349620653480L;/*** The object on which the Event initially occurred.*/protected transient Object source;/*** Constructs a prototypical Event.** @param source The object on which the Event initially occurred.* @exception IllegalArgumentException if source is null.*/public EventObject(Object source) {if (source == null)throw new IllegalArgumentException("null source");this.source = source;}/*** The object on which the Event initially occurred.** @return The object on which the Event initially occurred.*/public Object getSource() {return source;}/*** Returns a String representation of this EventObject.** @return A a String representation of this EventObject.*/public String toString() {return getClass().getName() + "[source=" + source + "]";}}
EvenObject是JDK给我们规定的一个统一的事件源。EvenObject类中定义了一个事件源以及获取事件源的get方法。
下面就分析一下Servlet Listener的运行流程。
目前, Servlet中存在6种两类事件的监听器接口, 具体如下图:
具体触发情境如下表:

我们以ServletRequestAttributeListener为例, 来分析一下此处事件驱动的流程。
首先一个Servlet中, HttpServletRequest调用setAttrilbute方法时, 实际上是调用的org.apache.catalina.connector.request#setAttrilbute方法。 我们看下它的源码:
public void setAttribute(String name, Object value) {...//上面的逻辑代码已省略// 此处即通知监听者notifyAttributeAssigned(name, value, oldValue);}
下面是notifyAttributeAssigned(String name, Object value, Object oldValue)的源码
private void notifyAttributeAssigned(String name, Object value,Object oldValue) {//从容器中获取webAPP中定义的Listener的实例对象Object listeners[] = context.getApplicationEventListeners();if ((listeners == null) || (listeners.length == 0)) {return;}boolean replaced = (oldValue != null);//创建相关事件对象ServletRequestAttributeEvent event = null;if (replaced) {event = new ServletRequestAttributeEvent(context.getServletContext(), getRequest(), name, oldValue);} else {event = new ServletRequestAttributeEvent(context.getServletContext(), getRequest(), name, value);}//遍历所有监听器列表, 找到对应事件的监听器for (int i = 0; i < listeners.length; i++) {if (!(listeners[i] instanceof ServletRequestAttributeListener)) {continue;}//调用监听器的方法, 实现监听操作ServletRequestAttributeListener listener =(ServletRequestAttributeListener) listeners[i];try {if (replaced) {listener.attributeReplaced(event);} else {listener.attributeAdded(event);}} catch (Throwable t) {ExceptionUtils.handleThrowable(t);context.getLogger().error(sm.getString("coyoteRequest.attributeEvent"), t);// Error valve will pick this exception up and display it to userattributes.put(RequestDispatcher.ERROR_EXCEPTION, t);}}}
上面的例子很清楚的看出ServletRequestAttributeListener是如何调用的。用户只需要实现监听器接口就行。Servlet中的Listener几乎涵盖了Servlet整个生命周期中你感兴趣的事件, 灵活运用这些Listenser可以使程序更加灵活。
观察者模式定义了对象之间一对多的关系, 当一个对象(被观察者)的状态改变时, 依赖它的对象都会收到通知。可以应用到发布——订阅, 变化——更新这种业务场景中。
观察者和被观察者之间用松耦合的方式, 被观察者不知道观察者的细节, 只知道观察者实现了接口。
事件驱动模型更加灵活,但也是付出了系统的复杂性作为代价的,因为我们要为每一个事件源定制一个监听器以及事件,这会增加系统的负担。