观察者模式
观察者模式是使用频率比较高的设计模式之一,它用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应。在观察者模式中发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任何相互关系,可以根据需要增加和删除观察者,使得系统更易于扩展。
观察者的别名有发布-订阅(Publish-Subscribe)模式、模型-视图(Model-View)模式、源-监听器(Source-Listener)模式、从属者(Dependents)模式
观察者模式结构:
观察者模式结构中通常包含观察目标和观察者两个继承层次,其结构如图:
- Subject(目标):目标又称主题,它是指被观察的对象。在目标中定义了一个观察者集合一个观察目标可以接受任意数量的观察者来观察(一对多),它提供了一系列方法来增加和删除观察者对象(可以这么说,是由目标去选择观察者来观察它,换句话说就是目标被观察者订阅了。就像你买报纸,报纸作为目标,你是观察者,报纸被你订阅了,所以报纸的动态都会被你观察到),同时它定义了通知方法notify()。目标类可以使接口,也可以使抽象类或者具体类。
- ConcreteSubject(具体目标):具体目标是目标类的子类,它通常包含经常发生改变的数据,当它的状态发生改变时将向它的各个观察者发出通知;同时它还实现了在目标类中定义的抽象业务逻辑方法(如果有),如果无需扩展目标类,则具体目标类可以省略。
- Observer(观察者):观察者将堆观察目标的改变作出反应,观察者一般定义为接口,该接口声明了更新数据的方法update(),因此又称为抽象观察者。
- ConcreteObserver(具体观察者):在具体观察者中维护一个指向具体目标对象的引用,它存储了具体观察者的有关状态,这些状态需要和具体目标的状态保存一致;它实现了在抽象观察者Observer中定义的update()方法。通常在实现时可以调用具体目标类的attach()方法将自己添加到目标类的集合中或通过detach()方法将自己从目标类的集合中删除(通俗易懂的说就是订阅和取消订阅,这里可能你会问,前面目标类不是选择观察者吗,为什么这里还说具体观察者去订阅和取消订阅,后面代码你看完就懂了)。
观察者模式实现:
抽象目标类(Subject)典型代码如下:
public abstract class Subject{
//定义一个观察者集合用于存储所有观察者对象
protected ArrayList observers<Observer> = new ArrayList();
//订阅方法,用于向观察者集合中增加一个观察者
public void attach(Observer observer){
observers.add(observer);
}
//取消订阅方法,用于从观察者集合中删除一个观察者
public void detach(Observer observer){
observers.remove(observer);
}
//声明抽象通知方法
public abstract void notify();
}
具体目标类ConcreteSubject是实现抽象目标类Subject的一个具体子类,其典型代码如下:
public class ConcreteSubject extends Subject{
//实现通知方法
public void notify(){
//通知观察者集合,调用每一个观察者的响应集合
for(Object object :observers){
((Observer)obs).update();
}
}
}
抽象观察者角色一般定义为一个接口,通常只声明一个update()方法,为不同观察者的更新行为定义相同的接口,这个方法在其子类中实现,不同的观察者具有不同的相应方法。抽象观察者Observer的典型代码如下:
public interface Observer{
//声明响应方法
public void update();
}
在具体观察者ConcreteObserver这实现update()方法,其典型代码如下:
public class ConcreteObserver implements Observer{
//实现响应方法
public void update(){
//具体响应代码
}
}
客户端调用代码片段如下:(注意看此处目标被观察者订阅代码)
...
Subject subject = new ConcreteSubject(); //创建具体目标
Observer observer = new ConcreteObserver(); //创建具体观察者
subject.attach(observer); //目标被观察者订阅
subject.notify(); //目标通知观察者
...
subject.detach(observer); //目标被观察者取消订阅