[关闭]
@pastqing 2015-10-19T15:58:08.000000Z 字数 7065 阅读 2797

java设计模式——观察者模式

java 设计模式


一、观察者模式(Observer)的定义:

观察者模式又称为订阅—发布模式,在此模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来事件处理系统。

1、观察者模式的一般结构

首先看下观察者模式的类图描述:
此处输入图片的描述
观察者模式的角色如下:

从这个类图可以看出, 主题类中维护了一个实现观察者接口的类列表, 主题类通过这个列表来对观察者进行一系列的增删改操作。观察者类也可以主动调用update方法来了解获取主题类的状态更新信息。

以上的类图所描述的只是基本的观察者模式的思想, 有很多不足。比如作为观察者也可以主动订阅某类主题等。下面的例子将进行一些改动, 以便适用具体的业务逻辑。

2、观察者模式示例

我们构建一个观察者和主题类, 观察者可以主动订阅主题或者取消主题。主题类统一被一个主题管理者所管理。下面给出类图:
Observer.png-221.4kB

  1. public interface Subject {
  2. //注册一个observer
  3. public void register(Observer observer);
  4. //移除一个observer
  5. public void remove(Observer observer);
  6. //通知所有观察者
  7. public void notifyObservers();
  8. //获取主题类要发布的消息
  9. public String getMessage();
  10. }
  1. public class MySubject implements Subject {
  2. private List<Observer> observers;
  3. private boolean changed;
  4. private String message;
  5. //对象锁, 用于同步更新观察者列表
  6. private final Object mutex = new Object();
  7. public MySubject() {
  8. observers = new ArrayList<Observer>();
  9. changed = false;
  10. }
  11. @Override
  12. public void register(Observer observer) {
  13. if (observer == null)
  14. throw new NullPointerException();
  15. //保证不重复
  16. if (!observers.contains(observer))
  17. observers.add(observer);
  18. }
  19. @Override
  20. public void remove(Observer observer) {
  21. observers.remove(observer);
  22. }
  23. @Override
  24. public void notifyObservers() {
  25. // temp list
  26. List<Observer> tempObservers = null;
  27. synchronized (mutex) {
  28. if (!changed)
  29. return;
  30. tempObservers = new ArrayList<>(this.observers);
  31. this.changed = false;
  32. }
  33. for(Observer obj : tempObservers) {
  34. obj.update();
  35. }
  36. }
  37. //主题类发布新消息
  38. public void makeChanged(String message) {
  39. System.out.println("The Subject make a change: " + message);
  40. this.message = message;
  41. this.changed = true;
  42. notifyObservers();
  43. }
  44. @Override
  45. public String getMessage() {
  46. return this.message;
  47. }
  48. }

ConcerteSubject做出更新时, 就通知列表中的所有观察者, 并且调用观察者update方法以实现接受通知后的逻辑。这里注意notifyObservers中的同步块。在多线程的情况下, 为了避免主题类发布通知时, 其他线程对观察者列表的增删操作, 同步块中用一个临时List来获取当前的观察者列表。

  1. public class SubjectManagement {
  2. //一个记录 名字——主题类 的Map
  3. private Map<String, Subject> subjectList = new HashMap<String, Subject>();
  4. public void addSubject(String name, Subject subject) {
  5. subjectList.put(name, subject);
  6. }
  7. public void addSubject(Subject subject) {
  8. subjectList.put(subject.getClass().getName(), subject);
  9. }
  10. public Subject getSubject(String subjectName) {
  11. return subjectList.get(subjectName);
  12. }
  13. public void removeSubject(String name, Subject subject) {
  14. }
  15. public void removeSubject(Subject subject) {
  16. }
  17. //singleton
  18. private SubjectManagement() {}
  19. public static SubjectManagement getInstance() {
  20. return SubjectManagementInstance.instance;
  21. }
  22. private static class SubjectManagementInstance {
  23. static final SubjectManagement instance = new SubjectManagement();
  24. }
  25. }

主题类管理器的作用就是在观察者订阅某个主题时, 获取此主题的实例对象。

  1. public interface Observer {
  2. public void update();
  3. public void setSubject(Subject subject);
  4. }
  1. public class MyObserver implements Observer {
  2. private Subject subject;
  3. // get the notify message from Concentrate Subject
  4. @Override
  5. public void update() {
  6. String message = subject.getMessage();
  7. System.out.println("From Subject " + subject.getClass().getName()
  8. + " message: " + message);
  9. }
  10. @Override
  11. public void setSubject(Subject subject) {
  12. this.subject = subject;
  13. }
  14. // subcirbe some Subject
  15. public void subscribe(String subjectName) {
  16. SubjectManagement.getInstance().getSubject(subjectName).register(this);
  17. }
  18. // cancel subcribe
  19. public void cancelSubcribe(String subjectName) {
  20. SubjectManagement.getInstance().getSubject(subjectName).remove(this);
  21. }
  22. }
  1. public class ObserverTest {
  2. private static MySubject writer;
  3. @BeforeClass
  4. public static void setUpBeforeClass() throws Exception {
  5. writer = new MySubject();
  6. //添加一个名为Linus的作家
  7. SubjectManagement.getInstance().addSubject("Linus",writer);
  8. }
  9. @Test
  10. public void test() {
  11. //定义几个读者
  12. MyObserver reader1 = new MyObserver();
  13. MyObserver reader2 = new MyObserver();
  14. MyObserver reader3 = new MyObserver();
  15. reader1.setSubject(writer);
  16. reader2.setSubject(writer);
  17. reader3.setSubject(writer);
  18. reader1.subscribe("Linus");
  19. reader2.subscribe("Linus");
  20. reader3.subscribe("Linus");
  21. writer.makeChanged("I have a new Changed");
  22. reader1.update();
  23. }
  24. }

以上就是观察者模式的小示例。可以看出每个主题类都要维护一个相应的观察者列表, 这里可以根据具体主题的抽象层次进一步抽象, 将这种聚集放到一个抽象类中去实现, 来共同维护一个列表, 当然具体操作要看实际的业务逻辑。

二、Servlet中的Listener

再说Servlet中的Listener之前, 先说说观察者模式的另一种形态——事件驱动模型。与上面提到的观察者模式的主题角色一样, 事件驱动模型包括事件源, 具体事件, 监听器, 具体监听器
Servlet中的Listener就是典型的事件驱动模型
JDK中有一套事件驱动的类, 包括一个统一的监听器接口和一个统一的事件源, 源码如下:

  1. /**
  2. * A tagging interface that all event listener interfaces must extend.
  3. * @since JDK1.1
  4. */
  5. public interface EventListener {
  6. }

这是一个标志接口, JDK规定所有监听器必须继承这个接口

  1. public class EventObject implements java.io.Serializable {
  2. private static final long serialVersionUID = 5516075349620653480L;
  3. /**
  4. * The object on which the Event initially occurred.
  5. */
  6. protected transient Object source;
  7. /**
  8. * Constructs a prototypical Event.
  9. *
  10. * @param source The object on which the Event initially occurred.
  11. * @exception IllegalArgumentException if source is null.
  12. */
  13. public EventObject(Object source) {
  14. if (source == null)
  15. throw new IllegalArgumentException("null source");
  16. this.source = source;
  17. }
  18. /**
  19. * The object on which the Event initially occurred.
  20. *
  21. * @return The object on which the Event initially occurred.
  22. */
  23. public Object getSource() {
  24. return source;
  25. }
  26. /**
  27. * Returns a String representation of this EventObject.
  28. *
  29. * @return A a String representation of this EventObject.
  30. */
  31. public String toString() {
  32. return getClass().getName() + "[source=" + source + "]";
  33. }
  34. }

EvenObject是JDK给我们规定的一个统一的事件源EvenObject类中定义了一个事件源以及获取事件源的get方法。

下面就分析一下Servlet Listener的运行流程。

1、Servlet Listener的组成

目前, Servlet中存在6种两类事件的监听器接口, 具体如下图:
ServletListener.png-127kB
具体触发情境如下表:
ServletListener _2.png-312.2kB

2、一个具体的Listener触发过程

我们以ServletRequestAttributeListener为例, 来分析一下此处事件驱动的流程。

首先一个Servlet中, HttpServletRequest调用setAttrilbute方法时, 实际上是调用的org.apache.catalina.connector.request#setAttrilbute方法。 我们看下它的源码:

  1. public void setAttribute(String name, Object value) {
  2. ...
  3. //上面的逻辑代码已省略
  4. // 此处即通知监听者
  5. notifyAttributeAssigned(name, value, oldValue);
  6. }

下面是notifyAttributeAssigned(String name, Object value, Object oldValue)的源码

  1. private void notifyAttributeAssigned(String name, Object value,
  2. Object oldValue) {
  3. //从容器中获取webAPP中定义的Listener的实例对象
  4. Object listeners[] = context.getApplicationEventListeners();
  5. if ((listeners == null) || (listeners.length == 0)) {
  6. return;
  7. }
  8. boolean replaced = (oldValue != null);
  9. //创建相关事件对象
  10. ServletRequestAttributeEvent event = null;
  11. if (replaced) {
  12. event = new ServletRequestAttributeEvent(
  13. context.getServletContext(), getRequest(), name, oldValue);
  14. } else {
  15. event = new ServletRequestAttributeEvent(
  16. context.getServletContext(), getRequest(), name, value);
  17. }
  18. //遍历所有监听器列表, 找到对应事件的监听器
  19. for (int i = 0; i < listeners.length; i++) {
  20. if (!(listeners[i] instanceof ServletRequestAttributeListener)) {
  21. continue;
  22. }
  23. //调用监听器的方法, 实现监听操作
  24. ServletRequestAttributeListener listener =
  25. (ServletRequestAttributeListener) listeners[i];
  26. try {
  27. if (replaced) {
  28. listener.attributeReplaced(event);
  29. } else {
  30. listener.attributeAdded(event);
  31. }
  32. } catch (Throwable t) {
  33. ExceptionUtils.handleThrowable(t);
  34. context.getLogger().error(sm.getString("coyoteRequest.attributeEvent"), t);
  35. // Error valve will pick this exception up and display it to user
  36. attributes.put(RequestDispatcher.ERROR_EXCEPTION, t);
  37. }
  38. }
  39. }

上面的例子很清楚的看出ServletRequestAttributeListener是如何调用的。用户只需要实现监听器接口就行。Servlet中的Listener几乎涵盖了Servlet整个生命周期中你感兴趣的事件, 灵活运用这些Listenser可以使程序更加灵活。

三、总结

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注