观察者模式【Observer Pattern】,什么是观察者模式?作用?优缺点?观察者模式实现?

目录


设计模式专栏目录(点击进入…)



什么是观察者模式?

​ 观察者模式 (Observer Pattern) 又叫发布-订阅模式 (Publish/Subscribe),是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

观察者模式的核心是将观察者与主题对象解耦,以类似于消息、广播发送的机制联动两者,使被观察者的变动能通知到感兴趣的观察者们,从而做出相应的响应。


观察者模式作用?

创建了对象间的一种一对多的依赖关系,当一个对象状态改变时,所有依赖于它的对象都会得到通知并自动更新。


观察者模式优缺点

优点

抽象耦合:观察者和主题之间是抽象耦合的。
触发机制:建立了一套状态改变时的触发和通知机制。

缺点

性能问题:如果观察者众多,通知过程可能耗时。
循环依赖:可能导致循环调用和系统崩溃。
缺乏变化详情:观察者不知道主题如何变化,只知道变化发生。


观察者模式包含角色

(1)抽象主题 (Subject)

指被观察的对象 (Observable)。该角色是一个抽象类或接口,定义了增加、删除、通知观察者对象的方法

(2)具体主题 (Concrete Subject)

具体被观察者,当其内部状态变化时,会通知已注册的观察者

(3)抽象观察者 (Observer)

定义了响应通知的更新方法

(4)具体观察者 (Concrete Observer)

在得到状态更新时,会自动做出响应

主题(Subject):也称为被观察者或可观察者,它是具有状态的对象,并维护着一个观察者列表。主题提供了添加、删除和通知观察者的方法。
观察者(Observer):观察者是接收主题通知的对象。观察者需要实现一个更新方法,当收到主题的通知时,调用该方法进行更新操作。
具体主题(Concrete Subject):具体主题是主题的具体实现类。它维护着观察者列表,并在状态发生改变时通知观察者。
具体观察者(Concrete Observer):具体观察者是观察者的具体实现类。它实现了更新方法,定义了在收到主题通知时需要执行的具体操作。


观察者模式实现方式

(1)定义观察者接口:包含一个更新方法。
(2)创建具体观察者:实现观察者接口,定义接收到通知时的行为。
(3)定义主题接口:包含添加、删除和通知观察者的方法。
(4)创建具体主题:实现主题接口,管理观察者列表,并在状态改变时通知它们。


观察者模式实现

《孙子兵法》有云:“知彼知己,百战不殆;不知彼而知己,一胜一负;不 知彼,不知己,每战必殆”,

那怎么才能知己知彼呢?
知己是很容易的,自己的军队嘛,很容易知道。

那怎么知彼呢?
安插间谍是很好的一个办法。 韩非子大家都应该记得吧,法家的代表人物,主张建立法制社会,实施重罚制度,真是非常有远见呀,看看现在社会在呼吁什么,建立法制化的社会,在 2000 多年前就已经提出了。大家可能还不知道,法家还有一个非常重要的代表人物,李斯,对,就是李斯,秦国的丞相,最终被残忍的车裂的那位,李斯和韩非子都是荀子的学生,李斯是师兄,韩非子是师弟,若干年后,李斯成为最强诸侯秦国的上尉,致力于统一全国,于是安插了间谍到各个国家的重要人物的身边,以获取必要的信息,韩非子作为韩国的重量级人物,身边自然没少间谍了,韩非子早饭吃的什么,中午放了几个 P,晚上在做什么娱乐,李斯都了如指掌,那可是相隔千里!怎么做到的呢?间谍呀!

在这里插入图片描述

1、实现Observable(被观察者)

JDK 中提供了 :java.util.Observable 实现类和 java.util.Observer 接口。

package com.uhhe.common.design.observer;

import java.util.Observable;

/**
 * 韩非子,李斯的师弟,韩国的重要人物
 *
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/3/1 17:06
 */
public class HanFeiZi extends Observable {

    /**
     * 韩非子要吃饭了
     */
    public void haveBreakfast() {
        System.out.println("韩非子:开始吃饭了...");
        // 通知所有的观察者
        super.setChanged();
        this.notifyObservers("韩非子在吃饭");
    }

    /**
     * 韩非子开始娱乐了,古代人没啥娱乐,你能想到的就那么多
     */
    public void haveFun() {
        System.out.println("韩非子:开始娱乐了...");
        super.setChanged();
        this.notifyObservers("韩非子在娱乐");
    }

}

2、实现Observer(李斯、王斯、刘斯)

李斯

package com.uhhe.common.design.observer;

import java.util.Observable;
import java.util.Observer;

/**
 * 李斯这个人,是个观察者,只要韩非子一有动静,这边就知道
 *
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/3/1 17:09
 */
public class LiSi implements Observer {

    /**
     * 首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报
     *
     * @param observable 观察者
     * @param obj        报告内容
     */
    @Override
    public void update(Observable observable, Object obj) {
        System.out.println("李斯:观察到李斯活动,开始向老板汇报了...");
        this.reportToQiShiHuang(obj.toString());
        System.out.println("李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...\n");
    }

    /**
     * 汇报给秦始皇
     *
     * @param reportContext 报告内容
     */
    private void reportToQiShiHuang(String reportContext) {
        System.out.println("李斯:报告,秦老板!韩非子有活动了--->" + reportContext);
    }

}

刘斯

package com.uhhe.common.design.observer;

import java.util.Observable;
import java.util.Observer;

/**
 * 刘斯这个人,是个观察者,只要韩非子一有动静,这边就知道
 *
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/3/1 17:11
 */
public class LiuSi implements Observer {

    @Override
    public void update(Observable observable, Object obj) {
        //刘斯,观察到韩非子活动后,自己也做一定得事情
        System.out.println("刘斯:观察到韩非子活动,开始动作了...");
        this.happy(obj.toString());
        System.out.println("刘斯:真被乐死了\n");
    }

    private void happy(String context) {
        //一看韩非子有变化,他就快乐
        System.out.println("刘斯:因为" + context + ",——所以我快乐呀!");
    }

}

王斯

package com.uhhe.common.design.observer;

import java.util.Observable;
import java.util.Observer;

/**
 * 王斯,也是观察者,杜撰的人名
 *
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/3/1 17:11
 */
public class WangSi implements Observer {

    @Override
    public void update(Observable observable, Object obj) {
        // 王斯,看到韩非子有活动,自己就受不了
        System.out.println("王斯:观察到韩非子活动,自己也开始活动了...");
        this.cry(obj.toString());
        System.out.println("王斯:真真的哭死了...\n");
    }

    private void cry(String context) {
        // 一看李斯有活动,就哭,痛哭
        System.out.println("王斯:因为" + context + ",——所以我悲伤呀!");
    }

}

3、Client(使用观察者)

package com.uhhe.common.design.observer;

import java.util.Observer;

/**
 * 使用观察者
 * 
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/3/1 17:04
 */
public class Client {

    /**
     * 观察者模式【Observer Pattern】
     * 观察者模式有一个变种叫做发布/订阅模型(Publish/Subscribe)
     * <p>
     * 观察者模式在实际项目的应用中非常常见
     * 比如: 到 ATM 机器上取钱,多次输错密码,卡就会被 ATM 吞掉,吞卡动作发生的时候,会触发哪些事件呢?
     * 第一,摄像头连续快拍
     * 第二,通知监控系统,吞卡发生;
     * 第三,初始化 ATM 机屏幕,返回最初状态
     * 不能因为就吞了一张卡,整个 ATM 都不能用了吧,一般前两个动作都是通过观察者模式来完成的
     * <p>
     * 广播链的问题?
     * 如果你做过数据库的触发器,你就应该知道有一个触发器链的问题,比如表 A 上写了一个触发器,
     * 内容是一个字段更新后更新表 B 的一条数据,而表 B 上也有个触发器,要更新表 C,表 C 也有
     * 触发器…,完蛋了,这个数据库基本上就毁掉了!我们的观察者模式也是一样的问题,一个观察者可以有双
     * 重身份,即使观察者,也是被观察者,这没什么问题呀,但是链一旦建立,这个逻辑就比较复杂,可维护
     * 性非常差,根据经验建议,在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消
     * 息最多转发一次(传递两次),这还是比较好控制的
     * <p>
     * 异步处理问题?
     * 这个 EJB 是一个非常好的例子,被观察者发生动作了,观察者要做出回应,如果观察者比较多,
     * 而且处理时间比较长怎么办?那就用异步呗,异步处理就要考虑线程安全和队列的问题,这个
     * 大家有时间看看 Message Queue,就会有更深的了解。
     * <p>
     * 工厂方法模式的时候用到了 ClassUtils 这个类,其中有一个方法就是根据接口查找到所有的实现类,问题解决了吧!
     * 可以查找到所有的观察者,然后全部加进来,以后要是新增加观察者也没有问题呀,程序那真是一点都不用改了
     */
    public static void main(String[] args) {
        // 三个观察者产生出来
        Observer liSi = new LiSi();
        Observer wangSi = new WangSi();
        Observer liuSi = new LiuSi();

        // 定义出韩非子
        HanFeiZi hanFeiZi = new HanFeiZi();

        // 后人根据历史,描述这个场景,有三个人在观察韩非子
        hanFeiZi.addObserver(liSi);
        hanFeiZi.addObserver(wangSi);
        hanFeiZi.addObserver(liuSi);

        // 然后这里我们看看韩非子在干什么
        hanFeiZi.haveBreakfast();
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未禾

您的支持是我最宝贵的财富!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值