10、观察者模式
观察者模式:
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象(依赖它的对象),使它们能够自动更新自己。观察者模式属于行为型模式。
观察者模式又叫:
发布——订阅(Publish/Subscribe)模式
模型——视图(Model/View)模式
源——监听器(Source/Listener)模式
从属者(Dependents)模式
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
观察者模式UML结构图:
基本代码(如UML图一):
abstract class Subject //只有增删观察者和通知方法
{
private IList<Observer> observers = new List<Observer>();
//增加观察者
public void Attach(Observer observer)
{
observers.Add(observer);
}
//移除观察者
public void Detach(Observer observer)
{
observers.Remove(observer);
}
//通知
public void Notify()
{
foreach (Observer o in observers)
{
o.Update();
}
}
}
//具体通知者
class ConcreteSubject : Subject //具体通知者(被观察者),继承抽象通知者,可直接利用抽象通知者的方法
{
private string subjectState;
//具体通知者状态
public string SubjectState
{
get { return subjectState; }
set { subjectState = value; }
}
}
abstract class Observer
{
public abstract void Update();
}
class ConcreteObserver : Observer
{
private string name;
private string observerState;
private ConcreteSubject subject;
public ConcreteObserver(
ConcreteSubject subject, string name)
{
this.subject = subject;
this.name = name;
}
//更新
public override void Update()
{
observerState = subject.SubjectState;
Console.WriteLine("观察者{0}的新状态是{1}",
name, observerState);
}
public ConcreteSubject Subject
{
get { return subject; }
set { subject = value; }
}
}
class Program
{
static void Main(string[] args)
{
ConcreteSubject s = new ConcreteSubject();
s.Attach(new ConcreteObserver(s, "X"));
s.Attach(new ConcreteObserver(s, "Y"));
s.Attach(new ConcreteObserver(s, "Z"));
s.SubjectState = "ABC";
s.Notify();
Console.Read();
}
}
例(股票行情变动):
代码实现:
import java.util.Observer;
public interface Subject {
void Attach(Observer observer);
void Detach(Observer observer);
void Notify();
}
import java.util.Observer;
/*
* //实现(这里以java.util自带的Observable类为基础进行继承实现)
* //(一开始打算使用引用实现,发现change是受保护的,不可视,因此无法更改setchange()就无法进行消息通知,改用继承
* //Subject接口和Subjectable实现其实都可以采用Observable类进行直接实现
*/
//继承操作会变更模式结构,有点不合理,采用Observable进行继承后然后组合以保障模式结构
public class Subjectable implements Subject{
private Observable observable = new Observable();
private String action; //发生的事件(要通知的消息)
public Subjectable() {}
public Subjectable(Observable observable, String action) {
this.observable = observable;
this.action = action;
}
public Subjectable(String action) {
this.action = action;
}
public Observable getObservable() {
return observable;
}
public void setObservable(Observable observable) {
this.observable = observable;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
//增加观察者
@Override
public void Attach(Observer observer) {
observable.addObserver(observer);
}
//减少观察者
@Override
public void Detach(Observer observer) {
observable.deleteObserver(observer);
}
//观察者通知接收者
@Override
public void Notify()
{
observable.setChanged();
observable.notifyObservers();
}
}
public class Observable extends java.util.Observable{
public Observable() {
super();
}
//setChanged()不可视,采用继承方式修改关键字变为可视
@Override
public synchronized void setChanged() {
super.setChanged();
}
}
import java.util.Observable;
import java.util.Observer;
public class ObserverDecline implements Observer{ //价格降低
private Subjectable subjectable;
public ObserverDecline(Subjectable subjectable) {
this.subjectable = subjectable;
}
public void update() {
System.out.println("股票B的价格下降了,快买!");
}
@Override
public void update(Observable o, Object arg) {
System.out.println(subjectable.getAction() + "股票B的价格下降了,快买!");
}
}
import java.util.Observable;
import java.util.Observer;
public class ObserverRise implements Observer{ //价格上升
//可能会在更新时显示其他属性(比如在炒股的一部分人或者看NBA的一部分人的姓名属性)
private Subjectable subjectable;
public ObserverRise(Subjectable subjectable) {
this.subjectable = subjectable;
}
// public void update() {
// System.out.println("股票A的价格上升了,快抛!");
// }
@Override
public void update(Observable o, Object arg) {
// jdk中提供了Observable观察者列表引用,与被观察者进行通信
//提供类型为Object的参数与调用者通信(该参数是调用者调用Observable实例的notifyObservers(Object obj)方法时传入的,当然也可以不传)
System.out.println(subjectable.getAction() + "股票A的价格上升了,快抛!");
}
}
import java.util.Observer;
public class Test {
public static void main(String[] args) {
//Observable observable = new Observable();
//Subjectable subjectable = new Subjectable(observable,"123");
Subjectable subjectable = new Subjectable();
Observer rise = new ObserverRise(subjectable);
Observer decline = new ObserverDecline(subjectable);
subjectable.Attach(rise);
subjectable.Attach(decline);
subjectable.setAction("股票行情有变动!");
subjectable.Notify();
}
}
/*
* 一、其实程序完全可以不那么复杂,
* 1.如果不调用java.util库而是自己写一个,实现观察者的增删以及updata动态通知更新的话应该会更简单
* (程序复杂的原因:java.util.Observable.setChange()是受保护的,
* 只能通过继承来进行修改以达到实现update的作用(如果setChange()不作修改,默认为false,无法继续进行update))
*
* 2.Subject和Subjectable实际上可以通过java.util.Observable作部分修改实现
*
* 二、买股票问题与老板(Tom)(只监视老板来没来)来了的问题之间差距:
* 老板(Tom)来了问题的情况是一定的,观察者绑定的员工会根据该消息做出相应的反应,比如停止炒股,停止游戏等
* 但是买股票问题情况并非是一致的,可能涉及到股票A,股票B,涨和跌也是存在的(不考虑涨幅或跌幅),这就要考虑不同情况进行监视,
* 然后获取到特定相关信息之后,观察者开始汇报(对不同情况进行判断后再确定分发消息的情况)
* 这里不细化,进行全局广播
*
* 错误记录:
* Exception in thread "main" java.lang.NullPointerException
* 空指针,可能存在内存未分配问题,只定义了但是没有new内存空间
*
* Exception in thread "main" java.lang.StackOverflowError
* 内存溢出,可能存在函数死循环自我调用问题,例如子类要实现父类时,方法名一致,但是漏写了super,导致子类方法一直循环调用,内存溢出
*
*/
当然上述被观察者作出反应并通知接收者(观察者)后,接收者要调用的方法名称不一定都是updata(),还有可能是其他方法,而且又可能通知者(被观察者)和接收者(观察者)之间根本就不互相知道,我们这时候就可以让客户端来决定通知谁。这时我们便引入了事件的委托(委托的内容在以后的介绍中我们再谈)。