对于许久不用的东西,容易忘记。百度许久,也未能找到自己所要。 从今日起,有些东西就记载下来,不仅方便自己,希望能帮到他人吧!
观察者模式(有时又被称为模型-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
定义
观察者模式(Observer)完美的将观察者和被观察的对象分离开。面向对象设计的一个原则是:系统中的每个类将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情,并且将他做好。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。
观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。
实现方式
观察者模式有很多实现方式,从根本上说,该模式必须包含两个角色:观察者和被观察对象。观察者和被观察者之间存在“观察”的逻辑关联,当被观察者发生改变的时候,观察者就会观察到这样的变化,并且做出相应的响应。
观察者: (Observer)将自己注册到被观察对象(Subject)中,被观察对象将观察者存放在一个容器(Container)里。
被观察: 被观察对象发生了某种变化,从容器中得到所有注册过的观察者,将变化通知观察者。
撤销观察: 观察者告诉被观察者要撤销观察,被观察者从容器中将观察者去除。
观察者将自己注册到被观察者的容器中时,被观察者不应该过问观察者的具体类型,而是应该使用观察者的接口。这样的优点是:假定程序中还有别的观察者,那么只要这个观察者也是相同的接口实现即可。一个被观察者可以对应多个观察者,当被观察者发生变化的时候,他可以将消息一一通知给所有的观察者。基于接口,而不是具体的实现——这一点为程序提供了更大的灵活性。
观察
实现观察者模式的时候要注意,观察者和被观察对象之间的互动关系不能体现成类之间的直接调用,否则就将使观察者和被观察对象之间紧密的耦合起来,从根本上违反面向对象的设计的原则。无论是观察者“观察”观察对象,还是被观察者将自己的改变“通知”观察者,都不应该直接调用。
例子
关二爷出名了,三国演义使得关二爷成为了武圣,各行各业皆跪拜关二哥,可是关二爷依旧是凡人一个,始终谨记–英雄?有人需要你,你就是英雄,没人需要你,你就是毒瘤。
关二爷十分低调,却引发了粉丝们的兴趣,于是各种媒体为了获得关二爷的动态实时监控关二爷。
代码如下:
JDK 中提供了 :java.util.Observable 实现类和 java.util.Observer 接口,我们不需重写,只需要实现即可。
/*
* Copyright (c) 1994, 1998, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util;
/**
* A class can implement the Observer
interface when it
* wants to be informed of changes in observable objects.
*
* @author Chris Warth
* @see java.util.Observable
* @since JDK1.0
*/
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an Observable object's
* notifyObservers
method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the notifyObservers
* method.
*/
void update(Observable o, Object arg);
}
/*
* Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
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.
*
* An observable object can have one or more observers. An observer
* may be any object that implements interface Observer. After an
* observable instance changes, an application calling the
* Observable
's notifyObservers
method
* causes all of its observers to be notified of the change by a call
* to their update
method.
*
* 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.
*
* Note that this notification mechanism has nothing to do with threads
* and is completely separate from the wait and notify
* mechanism of class Object.
*
* When an observable object is newly created, its set of observers is
* empty. Two observers are considered the same if and only if the
* equals method returns true for them.
*
* @author Chris Warth
* @see java.util.Observable#notifyObservers()
* @see java.util.Observable#notifyObservers(java.lang.Object)
* @see java.util.Observer
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
* @since JDK1.0
*/
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 null
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
* hasChanged
method, then notify all of its observers
* and then call the clearChanged
method to
* indicate that this object has no longer changed.
*
* Each observer has its update
method called with two
* arguments: this observable object and null
. In other
* words, this method is equivalent to:
*
* notifyObservers(null)
*
* @see java.util.Observable#clearChanged()
* @see java.util.Observable#hasChanged()
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
*/
public void notifyObservers() {
notifyObservers(null);
}
/**
* If this object has changed, as indicated by the
* hasChanged
method, then notify all of its observers
* and then call the clearChanged
method to indicate
* that this object has no longer changed.
*
* Each observer has its update
method called with two
* arguments: this observable object and the arg
argument.
*
* @param arg any object.
* @see java.util.Observable#clearChanged()
* @see java.util.Observable#hasChanged()
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
*/
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
*/
// Android-changed: Call out to hasChanged() to figure out if something changes.
// Upstream code avoids calling the nonfinal hasChanged() from the synchronized block,
// but that would break compatibility for apps that override that method.
// if (!changed)
if (!hasChanged())
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 Observable object as having been changed; the
* hasChanged method will now return true.
*/
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 hasChanged method will now return false.
* This method is called automatically by the
* notifyObservers
methods.
*
* @see java.util.Observable#notifyObservers()
* @see java.util.Observable#notifyObservers(java.lang.Object)
*/
protected synchronized void clearChanged() {
changed = false;
}
/**
* Tests if this object has changed.
*
* @return true
if and only if the setChanged
* method has been called more recently than the
* clearChanged
method on this object;
* false
otherwise.
* @see java.util.Observable#clearChanged()
* @see java.util.Observable#setChanged()
*/
public synchronized boolean hasChanged() {
return changed;
}
/**
* Returns the number of observers of this Observable object.
*
* @return the number of observers of this object.
*/
public synchronized int countObservers() {
return obs.size();
}
}
有兴趣的朋友,可以深入研究…
关二爷的类
不管关二爷是否被关注,关二爷始终要生活,所以关二爷有两个方法:1.读春秋;2.练习春秋刀法。
package model.com.hyco.ObserverPattern;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
/**
* 关二爷,超凡脱俗的人物
* 继承Observable,意味着被狗仔队监控了
*/
public class GuanEr extends Observable {
public void read() {
System.out.println("关二爷:开始读春秋...");
//通知所有的观察者
super.setChanged();
this.notifyObservers("关二爷读春秋...");
}
public void exercise() {
System.out.println("关二爷:开始练习春秋刀法...");
//通知所有的观察者
super.setChanged();
this.notifyObservers("关二爷开始联系春秋刀法");
}
}
某媒体 TX、YK,获取到关二爷的消息,立马发布,获取流量。
package model.com.hyco.ObserverPattern;
import java.util.Observable;
import java.util.Observer;
/**
* 某媒体 TX 当Observable 通知所有观察者后,会调用Observer 中的update()方法。研读源码可知。
*/
public class TX implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("TX 狗仔队:观察到关二爷的消息...");
exaggerate((String) arg);
}
public void exaggerate(String msg) {
System.out.println("TX 媒体 发布关二爷的消息 -->" + msg);
}
}
package model.com.hyco.ObserverPattern;
import java.util.Observable;
import java.util.Observer;
/**
* 某媒体 YK
*/
public class YK implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("YK 狗仔队:观察到关二爷的消息...");
exaggerate((String) arg);
}
public void exaggerate(String msg) {
System.out.println("YK 媒体 发布关二爷的消息 -->" + msg);
}
}
测试代码:
@Test
public void testObserver() {
//1.定义观察者
Observer tx = new TX();
Observer yk = new YK();
//2.定义关二爷 被观察者
GuanEr guanEr = new GuanEr();
//2.1注册
System.out.println("没有注册,获取不到媒体的消息");
guanEr.read();
System.out.println("注册");
guanEr.addObserver(tx);
guanEr.addObserver(yk);
//2.2 发布消息
guanEr.read();
System.out.println();
guanEr.exercise();
System.out.println();
//2.3注销
System.out.println("注销");
guanEr.deleteObserver(tx);
guanEr.deleteObserver(yk);
System.out.println("注销之后,获取不到媒体消息");
guanEr.read();
}
打印结果:
没有注册,获取不到媒体的消息
关二爷:开始读春秋...
注册
关二爷:开始读春秋...
YK 狗仔队:观察到关二爷的消息...
YK 媒体 发布关二爷的消息 -->关二爷读春秋...
TX 狗仔队:观察到关二爷的消息...
TX 媒体 发布关二爷的消息 -->关二爷读春秋...
关二爷:开始练习春秋刀法...
YK 狗仔队:观察到关二爷的消息...
YK 媒体 发布关二爷的消息 -->关二爷开始联系春秋刀法
TX 狗仔队:观察到关二爷的消息...
TX 媒体 发布关二爷的消息 -->关二爷开始联系春秋刀法
注销
注销之后,获取不到媒体消息
关二爷:开始读春秋...
总结:观察者模式有些类似无序广播,生活中有很多观察者模式。比如我的这篇博文,不管你是否关注,我还是会发布,只是你没有关注,便收不到消息,如果关注了,你会收到通知,取消关注,你又收不到消息。只需要把关注改成观察即可。或者某个应用的通知,也是观察者模式。例如某个粉丝群体,每个粉丝便是单独的观察者,而被关注的人,便是被观察者。