软件设计模式学习(二十三)观察者模式


观察者模式是一种常用的设计模式,在软件系统中对象并非孤立存在的,一个对象行为的改变可能会致使其余与之存在依赖关系的对象行为发生改变,观察者模式用于描述对象之间的依赖关系。java


模式动机

不少状况下,对象不是孤立存在的,想象这么一个场景:你和女友去旅行,晚上回到宾馆,女友穿着厚厚的大衣,从外表看上去就是个臃肿的包子。你没有反应,等到女友洗完澡裹着浴巾出来之后,你立马眼睛都瞪直了,活脱脱一个大色狼(不...不要盯着人家看了啦)设计模式

从例子中,咱们不难分离出两类角色,一类为观察者,如色眯眯的你,另外一类就是被观察者所观察的目标,如性感的女友。若是观察目标发生某个动做,观察者就会有响应。this

创建一种对象与对象之间的依赖关系,一个对象发生改变时会自动通知其余对象,其余对象将相应作出反应。发生改变的对象称为观察目标,被通知的对象称为观察者,一个观察目标能够对应多个观察者,并且观察者之间没有相互联系,能够根据须要增长和删除观察者,这就是观察者模式的模式动机。设计


模式定义

定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆获得通知并自动更新。观察者模式又叫发布-订阅模式(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)或从属者模式(Dependents)模式。观察者模式是一种对象行为型模式。code


模式分析

观察者模式描述了如何创建对象与对象之间的依赖关系,那么具体如何构造呢?server

这一模式的关键对象是观察目标和观察者,一个目标能够有任意多个与之相依赖的观察者,一旦目标状态发生变化,全部观察者都将获得通知。对象

做为对这个通知的响应,每一个观察者都将即时更新本身的状态,与目标状态同步,这种交互也称发布-订阅(publish-subscribe)。blog

目标是通知的发布者,它发出通知时不须要知道谁是它的观察者,能够有任意数目的观察订阅并接收通知。接口

由此看一下根据该模式得出的类图rem

抽象目标 Subject,典型代码以下

import java.util.*;
public abstract class Subject {
    
    // 定义一个集合存储任意数量的观察者对象
    protected ArrayList<Observer> observers = new ArrayList<Observer>();
    
    // 增长一个观察者
    public abstract void attach(Observer observer);
    // 删除一个观察者
    public abstract void detach(Observer observer);
    // 通知各个观察者并调用它们的 update() 方法
    public abstract void notifyObservers();
}

具体目标类 ConcreteSubject 是实现 Subject 的一个具体子类,典型代码以下

public class ConcreteSubject extends Subject {
    
    // 向集合中添加一个观察者
    public void attach(Observer observer) {
        observers.add(observer);
    }
    // 从集合中删除一个观察者
    public void detach(Observer observer) {
        observers.remove(observer);
    }
    // 循环调用集合中观察者的 updates() 方法
    public void notifyObservers() {
        for (Observer obs : observers) {
			obs.update();
        }
    }
}

也能够把 attach() 和 detach() 方法的实现放在 Subject 中,这样就无需每一个子类都去实现一次了(若是没有特别需求的话)。另外,它还能够实如今目标类中定义的抽象业务逻辑方法(若是有的话)

抽象观察者通常定义为一个接口,其中声明 update() 方法,这个方法在其子类中实现,不一样的观察者具备不一样的更新响应方法,典型代码以下

public interface Observer {
	public void update();
}

具体观察者 ConcreteObserver 中实现 update() 方法,其典型代码以下

public class ConcreteObserver implements Observer {
    public void update() {
        //	具体更新代码
    }
}

在使用时,客户端首先建立具体目标对象以及具体观察者对象(也可使用配置文件),而后,调用目标对象的 attach() 方法,将这个观察者对象在目标对象中登记,也就是将它加入到目标对象的观察者集合中

Subject subject = new ConcreteSubject();
Observer observer = new ConcreteObserver();
subject.attach(observer);
subject.notifyObservers();

客户端调用目标对象的 notifyObservers() 方法时,将调用在其观察者集合中注册的观察者对象的 update() 方法,实现状态的更新

固然,在有些复杂的状况下,具体观察者类 ConcreteObserver 的 update() 方法在执行时,须要使用到 ConcreteSubject 中的状态(属性)

举个例子,咱们常常会用可视化的图表(如柱状图、饼状图)来显示数据,一样的数据可能有不一样的图表显示方法(即不一样的具体观察者),若是数据发生改变,图标也应该实时作出改变,那天然的,图表在更新时确定须要用到新的数据吧

若是像上述而言,那么 ConcreteObserver 和 ConcreteSubject 之间还存在关联关系,在 ConcreteObserver 中定义一个 ConcreteSubject 实例,经过该实例获取存储在 ConcreteSubject 中的状态属性。但这样一来,系统的扩展性将受到必定影响,增长新的具体目标类有时候须要修改原有观察者的代码,在必定程度上违反了开闭原则,但若是原有观察者无须关联新增具体目标,则系统扩展性不受影响

// 具体目标类
public class ConcreteSubject extends Subject {
    
    // 方便起见,attahch() 等抽象方法在抽象层作了实现了
    
    // 具体目标类状态
    private int state;
    
    public int getState() {          
    	return state;                
	}                                
                                 
	public void setState(int state) {
    	this.state = state;          
	}                                
}
// 具体观察者类
public class ConcreteObserver implements Observer {
    
    private Subject subject = new ConcreteSubject();
    private int observerState;
    
    public void update() {
        observerState = subject.getState();
        //	具体更新代码
    }
}

模式优缺点

观察者模式的优势

  • 在观察目标和观察者之间创建一个抽象的耦合,观察目标不须要了解其具体观察者,只需知道它们都有一个共同的接口便可
  • 观察者模式支持广播通讯,观察目标会向全部注册的观察者发出通知,简化一对多系统设计的难度
  • 观察者模式符合开闭原则,增长新的观察者无须修改原有系统代码,在具体观察者和观察目标之间不存在关联关系的状况下,增长新的目标也很方便

观察者模式的缺点

  • 若是一个观察目标有不少直接和间接的观察者,将全部观察者都通知到会花费不少时间
  • 若是在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间的循环调用,可能致使系统崩溃
  • 观察者模式没有相应机制让观察者知道所观察目标是怎么发生变化的,而仅仅只是知道观察目标发生了变化

Java 对观察者模式的支持

Java 提供了 Observable 类以及 Observer 接口,构成对 Java 语言对观察者模式的支持

java.util.Observer 接口只定义一个方法,充当抽象观察者

public interface Observer {

    void update(Observable o, Object arg);
}

当观察目标状态发生变化时,该方法将被调用,在 Observer 的实现子类实现该 update() 方法,即具体观察者能够根据须要有不一样的更新行为。当调用观察目标类 Observable 的 notifyObservers() 方法时,将调用观察者类中的 update() 方法。

java.util.Observable 类充当观察目标类,定义了一个向量 Vector 来存储观察者对象。标记变量 changed 表示观察目标是否发生变化,true 表示发生变化,false 则表示对象再也不发生改变还已经通知了全部观察者对象,并调用了它们的 update() 方法。

咱们能够直接使用 Observer 接口和 Observable 类来做为观察者模式的抽象层,自定义具体的观察者类和观察目标类,更加方便地在 Java 语言中使用观察者模式。

相关文章
相关标签/搜索