观察者模式(Observer Pattern)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了观察者模式(Observer Pattern)相关的知识,希望对你有一定的参考价值。

参考技术A 观察者模式又称为发布订阅模式。一个发布者对应多个订阅者,一旦发布者的状态发生改变时,订阅者将收到订阅事件。

先看看一个生活中的例子:
我们使用想浏览Java相关的文章,于是我们点击订阅了[Java专题],当[Java专题]有新文章发布就会推送给我们,当然其他人也可以订阅[Java专题]并收到[Java专题]的推送。这就是观察者。 定义对象间的一对多关系,当一个对象的状态发生变化时,所依赖于它的对象都得到通知并主动更新。在观察者模式中,多个订阅者成为观察者(Observer),被观察的对象成为目标(Subject)。

实现观察者模式的方法不只一种,但是以包含Subject与Observer接口的类设计的做法最常见。(Java API 内置观察者模式用的是Observer接口与Observable类)

观察者模式UML图:

先定义观察者模式的接口

在观察者模式的实现上,有推模式和拉模式两种方式。

上面例子中
void updateByPush(Object obj) 就是推模式;
void updateByPull(Subject subject)就是拉模式

java.util包内包含最基本的Observer接口与Observable类(其实对应的就是Subject类)
我们看一下Observer源码

我们看到update更新方法有两个参数:Observable、Object,可见Java API 内置观察者模式同时支持[拉]和[取]

我们再来看看Observable类源码

注意Observable是一个类,而不是接口,这有一定的局限性。因为如果某个类想同时具有Observable类和另一个超类的行为,就会陷入两难,毕竟Java不支持多重继承。

有点需要特别提一下的就是,Java API 内置的Observable需要调用一下 setChanged();观察者才能收到推送,我们看一下源码,发现notifyObservers方法里有判断changed的状态为true才去通知观察者。

我们自己实现观察者模式的时候是没有这一点的,那加上这一个标志位有什么好处?好处就是更灵活,Observable类只提供这个boolean值来表明是否发生变化,而不定义什么叫变化,因为每个业务中对变化的具体定义不一样,因此子类自己来判断是否变化;该变量既提供了一种抽象(变与不变),同时提供了一种观察者更新状态的可延迟加载,通过后面的notifyObservers方法分析可知观察者是否会调用update方法,依赖于changed变量,因此即使被观察者在逻辑上发生改变了,只要不调用setChanged,update是不会被调用的。如果我们在某些业务场景不需要频繁触发update,则可以适时调用setChanged方法来延迟刷新。

阿里云折扣快速入口

观察者模式(Observer Pattern)

观察者模式

一、什么是观察者模式?

??观察者模式(别名--发布-订阅)是软件设计模式的一种。观察者模式属于行为型模式。(行为型模型-特别关注对象之间的通信)

??观察者模式(Observer)完美的将观察者和被观察的对象分离开。观察者设计模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动更新。

??易用和低耦合,保证高度的协作。

二、观察者模式的适用场景

  1. 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。
  2. 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。
  3. 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。

三、观察者模式的具体实现

结构图

技术分享图片

观察者模式中的四个重要角色
  1. 抽象被观察者角色(Observable):即一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个添加和删除观察者角色的接口。一般用一个抽象类和接口来实现。
  2. 抽象观察者角色(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
  3. 具体被观察者角色(ConcreteObservable):也就是一个具体的主题,当具体主题的内部状态改变时,给所有登记过的观察者发出通知。
  4. 具体观察者角色(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以使其自身状态和主题的状态保持一致。
java代码实现

1.定义一个抽象被观察者接口,添加、删除、通知观察者

package com.designpattern.observerPattern;

/**
 * 被观察者接口 Observable
 *
 * @author zhongtao on 2018/9/10
 */
public interface Observable {

    /**
     * 添加观察者
     */
    void addObserver(Observer observer);

    /**
     * 移除观察者
     */
    void removeObserver(Observer observer);

    /**
     * 通知观察者
     */
    void notifyObserver();
}

2.定义一个抽象观察者接口

package com.designpattern.observerPattern;

/**
 * 观察者接口 Observer
 *
 * @author zhongtao on 2018/9/10
 */
public interface Observer {

    /**
     * 更新消息
     */
    void update(String message);
}

3.实现被观察者,有一个List集合,用以保存注册的观察者,等需要通知观察者时,遍历该集合。

package com.designpattern.observerPattern;

import java.util.ArrayList;
import java.util.List;

/**
 * 具体被观察者
 *
 * @author zhongtao on 2018/9/10
 */
public class ConcreteObservable implements Observable {

    // 用来存储观察者对象的集合
    private List<Observer> list = new ArrayList<Observer>();

    //消息
    private String message;

    @Override
    public void addObserver(Observer observer) {
        list.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        if(!list.isEmpty()) {
            list.remove(observer);
        }
    }

    @Override
    public void notifyObserver() {
        for (Observer observer : list) {
            observer.update(message);
        }
    }

    /**
     * 设置更新消息
     * @param message
     */
    public void setNotifyMessage(String message){
        this.message = message;
        System.out.println("更新消息:" + message);
        notifyObserver();
    }
}

4.实现观察者

package com.designpattern.observerPattern;

/**
 * 具体实现观察者
 *
 * @author zhongtao on 2018/9/10
 */
public class User implements Observer {

    private String name;

    public User(String name){
        this.name = name;
    }

    @Override
    public void update(String message) {
        System.out.println(name + "你有新消息了:" + message);
    }
}

5.测试观察者模式

package com.designpattern.observerPattern;

import org.junit.Test;

/**
 * 观察者模式测试类
 *
 * @author zhongtao on 2018/9/10
 */
public class ObserverPatternTest {

    /**
     * 测试观察者模式
     */
    @Test
    public void testObserverPattern() {
        ConcreteObservable observable = new ConcreteObservable();

        User peter = new User("Peter");
        User make = new User("Make");
        User lina = new User("Lina");

        //添加观察者
        observable.addObserver(peter);
        observable.addObserver(make);
        observable.addObserver(lina);
        observable.setNotifyMessage("星际争霸2中人族太IMBA");

        System.out.println("-----分割线-----");
        observable.removeObserver(peter);
        observable.setNotifyMessage("星灵才是最IMBA的!");
    }
}

测试结果:当移除peter之后,peter就无法再收到推送消息了。

更新消息:星际争霸2中人族太IMBA
Peter你有新消息了:星际争霸2中人族太IMBA
Make你有新消息了:星际争霸2中人族太IMBA
Lina你有新消息了:星际争霸2中人族太IMBA
-----分割线-----
更新消息:星灵才是最IMBA的!
Make你有新消息了:星灵才是最IMBA的!
Lina你有新消息了:星灵才是最IMBA的!

四、观察者模式的优缺点

优点:
  1. 观察者和被观察者是抽象耦合的。
  2. 建立一套触发机制。
缺点:
  1. 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  2. 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  3. 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。



以上是关于观察者模式(Observer Pattern)的主要内容,如果未能解决你的问题,请参考以下文章

观察者模式(Observer Pattern)

观察者模式(Observer Pattern)

观察者模式-Observer Pattern

观察者模式(Observer Pattern)

设计模式之- 观察者模式(Observer Pattern)

设计模式之二:观察者模式(Observer Pattern)