好久没有写博客了,这段时间在项目中接触到了java设计模式中的观察者模式,因为之前从来没有接触过,所以特意花费了一点时间用来学习,在这里也把它记录下来,毕竟好记性不如烂笔头,闲言少叙,话不多说,书归正传。
一、概念(来自百度百科)
观察者模式:(有时又被称为模型(Model)-视图(View)模式、源-收听者(Listener)模式或从属者模式),是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
百度百科里的概念过于正式,换做自己的话来说,观察者模式就相当于java里面的一对多的关系,多的一方称之为观察者,可以是一,也可以是多,一的一方也被称为被观察者,用现实中的某样事物来比喻的话,个人觉得更像是报纸,被观察者就相当于报纸公司,它在报纸中发布各种信息,然后每一名读者,也就是购买报纸的人,就会收到这条信息。
二、案例
首先需要定义一个被观察者的抽象类
package com.eplugger.test; import java.util.ArrayList; import java.util.List; /** * 抽象公司类(作为被观察者,也就是消息发送者) */ public abstract class Company { // 观察者的集合,也就是订阅该报纸的所有读者 private List<Person> observers = new ArrayList<Person>(); private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Company(String name) { this.name = name; } // 新增观察者(读者) public void addObservers(Person observer){ observers.add(observer); } // 移除观察者(读者) public void removeObservers(Person observer){ observers.remove(observer); } // 通知观察者(读者) public void notifyObservers(){ for (Person observer : observers) { observer.update(); } } }
接着,定义观察者的抽象类
package com.eplugger.test; /** * 定义抽象观察者 * * */ public abstract class Person { private String name; private Company company; public String getName() { return name; } public Company getCompany() { return company; } public void setName(String name) { this.name = name; } public void setCompany(Company company) { this.company= company; } public Person(String name, Company company) { this.name = name; this.company = company; } public abstract void update(); }
下面是两者的具体实现类
package com.eplugger.test; /** * 具体的消息通知者(报社) * * */ public class Newspaper extends Company{ public Newspaper(String name){ super(name); } private String title; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } }
package com.eplugger.test; /** * 具体观察者(读者) * * */ public class Readers extends Person{ public Readers(String name, Newspaper newspaper) { super(name, newspaper); } @Override public void update(){ System.out.println(super.getName()+"...."+super.getCompany().getName()+"....通知...."); } }
测试代码
package com.eplugger.test; public class Test1 { public static void main(String[] args) { Newspaper newspaper = new Newspaper("新华日报"); Readers readers = new Readers("张三", newspaper); newspaper.removeObservers(readers); newspaper.notifyObservers(); } }
上面描述的这是一种最基本,最简单的观察者模式,是建立在所有的观察者都能抽象出一个公共抽象类的基础上,但是在实际的项目运用中,往往没有办法做到所有的观察者都能够抽象出同一个公共抽象类,这就得用到Java自带的Observer接口以提供对观察者模式的支持,同时也会运用到JDK来实现观察者模式。
在Java中,已经有现成的接口和类,对于观察者,也就是消息接收者,Java提供了一个接口,源码如下
package java.util; /** * A class can implement the <code>Observer</code> interface when it * wants to be informed of changes in observable objects. * */ public interface Observer { void update(Observable o, Object arg); }
对于被观察者,也就是消息的发布者,Java提供了一个抽象类,源码如下
package java.util; /** * This class represents an observable object, or "data" * in the model-view paradigm. It can be subclassed to represent an * object that the application wants to have observed. * <p> * An observable object can have one or more observers. An observer * may be any object that implements interface <tt>Observer</tt>. After an * observable instance changes, an application calling the * <code>Observable</code>'s <code>notifyObservers</code> method * causes all of its observers to be notified of the change by a call * to their <code>update</code> method. * <p> * The order in which notifications will be delivered is unspecified. * The default implementation provided in the Observable class will * notify Observers in the order in which they registered interest, but * subclasses may change this order, use no guaranteed order, deliver * notifications on separate threads, or may guarantee that their * subclass follows this order, as they choose. * <p> * Note that this notification mechanism is has nothing to do with threads * and is completely separate from the <tt>wait</tt> and <tt>notify</tt> * mechanism of class <tt>Object</tt>. * <p> * When an observable object is newly created, its set of observers is * empty. Two observers are considered the same if and only if the * <tt>equals</tt> method returns true for them. * */ public class Observable { private boolean changed = false; private Vector obs; /** Construct an Observable with zero Observers. */ public Observable() { obs = new Vector(); } /** * Adds an observer to the set of observers for this object, provided * that it is not the same as some observer already in the set. * The order in which notifications will be delivered to multiple * observers is not specified. See the class comment. * * @param o an observer to be added. * @throws NullPointerException if the parameter o is null. */ public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } /** * Deletes an observer from the set of observers of this object. * Passing <CODE>null</CODE> to this method will have no effect. * @param o the observer to be deleted. */ public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } /** * If this object has changed, as indicated by the * <code>hasChanged</code> method, then notify all of its observers * and then call the <code>clearChanged</code> method to * indicate that this object has no longer changed. * <p> * Each observer has its <code>update</code> method called with two * arguments: this observable object and <code>null</code>. In other * words, this method is equivalent to: * <blockquote><tt> * notifyObservers(null)</tt></blockquote> * */ public void notifyObservers() { notifyObservers(null); } /** * If this object has changed, as indicated by the * <code>hasChanged</code> method, then notify all of its observers * and then call the <code>clearChanged</code> method to indicate * that this object has no longer changed. * <p> * Each observer has its <code>update</code> method called with two * arguments: this observable object and the <code>arg</code> argument. * */ public void notifyObservers(Object arg) { /* * a temporary array buffer, used as a snapshot of the state of * current Observers. */ 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); } /** * Clears the observer list so that this object no longer has any observers. */ public synchronized void deleteObservers() { obs.removeAllElements(); } /** * Marks this <tt>Observable</tt> object as having been changed; the * <tt>hasChanged</tt> method will now return <tt>true</tt>. */ protected synchronized void setChanged() { changed = true; } /** * Indicates that this object has no longer changed, or that it has * already notified all of its observers of its most recent change, * so that the <tt>hasChanged</tt> method will now return <tt>false</tt>. * This method is called automatically by the * <code>notifyObservers</code> methods. * */ protected synchronized void clearChanged() { changed = false; } /** * Tests if this object has changed. * * @return <code>true</code> if and only if the <code>setChanged</code> * method has been called more recently than the * <code>clearChanged</code> method on this object; * <code>false</code> otherwise. */ public synchronized boolean hasChanged() { return changed; } /** * Returns the number of observers of this <tt>Observable</tt> object. * * @return the number of observers of this object. */ public synchronized int countObservers() { return obs.size(); } }
比较一下Java的源码和上述我自己写的区别,可以看到,源码中使用Vector而不是ArrayList,相比而言,Vector是线程安全的,其次,在添加和删除观察者时对两个方法使用了synchronized关键字,这都是因为多线程的原因,可以看出来,源码考虑的要全面的多。
利用Java提供的接口和抽象类观察者模式和上述我自写的差不多,就放一个小demo,简单的看一下就ok了。
Demo
package com.eplugger.demo; import java.util.Observable; import java.util.Observer; public class Readers implements Observer{ public Readers(Observable o){ o.addObserver(this); } @Override public void update(Observable o, Object arg) { System.out.println("名字是:" + ((Newspaper)o).getName()); } }
package com.eplugger.demo; import java.util.Observable; public class Newspaper extends Observable { private String name; public String getName() { return name; } public void setName(String name) { if (!this.name.equals(name)){ this.name = name; setChanged(); } notifyObservers(); } }
package com.eplugger.demo; public class Test { public static void main(String[] args) { Newspaper newspaper = new Newspaper(); Readers readers = new Readers(newspaper); newspaper.setName("新华日报"); } }
运用Java自带的接口和抽象类来实现观察者模式,其原理和上一个案例差不多。
三、总结
观察者模式的结构
观察者模式的优点:
1、实现了表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,抽象了接口,使得可以适应不同的表现层充当观察者角色。
2、在观察目标和观察者之间建立了一个抽象耦合,观察目标只需要维持一个抽象观察者的集合,无需了解具体的观察者。
3、支持广播通信,观察目标会向所有的观察者对象发送通知,简化了一对多系统设计的难度。
4、满足“开闭原则“的要求,增加新的具体观察者无需修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便。
观察者模式的缺点:
1、如果一个被观察者有着很多观察者,那么会耗费很长的时间。
2、没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
3、 Java中自带的观察者模式,被观察者是一个抽象类而不是接口,限制了复用能力。
应用场景:
一个对象的改变将会导致一个或多个对象的改变的情况,即一对多的程序设计。