软件设计模式修炼 -- 观察者模式

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

模式动机

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

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

建立一种对象与对象之间的依赖关系,一个对象发生改变时会自动通知其他对象,其他对象将相应做出反应。发生改变的对象称为观察目标,被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且观察者之间没有相互联系,可以根据需要增加和删除观察者,这就是观察者模式的模式动机。

模式定义

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

模式分析

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

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

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

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

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

抽象目标 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 语言中使用观察者模式。

(0)

相关推荐

  • 图解Java设计模式之观察者模式

    天气预报项目需求 1)气象站可以将每天测量到的湿度.温度.气压等等以公告的形式发布出去(比如发布到自己的网站或第三方). 2)需要设计开放型API,便于其他第三方也能接入气象站获取数据. 3)提供温度 ...

  • 行为型模式之观察者模式

    在现实世界中,许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变.例如,某种商品的物价上涨时会导致部分商家高兴,而消费者伤心. 在软件世界也是这样,例如 ...

  • 设计模式(20) 观察者模式

    观察者模式是一种平时接触较多的模式.它主要用于一对多的通知发布机制,当一个对象发生改变时自动通知其他对象,其他对象便做出相应的反应,同时保证了被观察对象与观察对象之间没有直接的依赖. GOF对观察者模 ...

  • 阿里面试官亲述:如何利用设计模式改善业务代码

    在业务部门的开发中,大多数的我们在完成的业务的各种需求和提供解决方案,很多场景下的我们通过 CRUD 就能解决问题,但是这样的工作对技术人的提升并不多,如何让自己从业务中解脱出来找到写代码的乐趣呢,我 ...

  • 软件设计模式修炼 -- 访问者模式

    访问者模式是一种较为复杂的行为型设计模式,它包含访问者和被访问元素两个主要组成部分,这些被访问的元素具有不同的类型,且不同的访问者可以对其进行不同的访问操作 模式动机 对于系统中某些对象,它们存储在同 ...

  • 软件设计模式修炼 -- 迭代器模式

    迭代器模式是一种使用频率非常高的设计模式,迭代器用于对一个聚合对象进行遍历.通过引入迭代器可以将数据的遍历功能从聚合对象中分离出来,聚合对象只负责存储数据,聚合对象只负责存储数据,而遍历数据由迭代器来 ...

  • 软件设计模式修炼 -- 解释器模式

    解释器是一种不常使用的设计模式,它用于描述如何构成一个简单的语言解释器,主要应用于使用面向对象语言开发的编译器和解释器设计.当我们需要开发一个新的语言时,可以考虑使用解释器模式 模式动机 如果在系统中 ...

  • PHP设计模式之观察者模式

    PHP设计模式之观察者模式 观察者,貌似在很多科幻作品中都会有这个角色的出现.比如我很喜欢的一部美剧<危机边缘>,在这个剧集中,观察者不停的穿越时空记录着各种各样的人或事.但是,设计模式中 ...

  • [PHP小课堂]PHP设计模式之观察者模式

    [PHP小课堂]PHP设计模式之观察者模式 关注公众号:[硬核项目经理]获取最新文章 添加微信/QQ好友:[DarkMatterZyCoder/149844827]免费得PHP.项目管理学习资料

  • 二十三种设计模式修炼手册

    不知不觉,在开发这条道路上摸爬打滚也有些年头了,偶尔回头看看以前写的代码,真可谓粗糙至极.当然了,那时候还是小白,代码写得难看些情有可原,不过现在可不能再用以前的标准去衡量自己了,因此掌握一些高级架构 ...

  • JAVA设计模式之观察者模式

    JAVA设计模式之观察者模式