【设计模式】观察者模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )

举报
韩曙亮 发表于 2022/01/12 22:56:37 2022/01/12
【摘要】 文章目录 一、观察者模式简介二、观察者模式适用场景三、观察者模式优缺点四、观察者模式代码示例1、被观察者2、观察者3、通知类4、测试类 五、JDK 中的观察者模式支持类1、Observable...





一、观察者模式简介



观察者模式 : 定义了 对象之间 一对多 的 依赖 , 令 多个 观察者 对象 同时 监听 某一个 主题对象 , 当 主题对象 发生改变时 , 所有的 观察者 都会 收到通知 并更新 ;

观察者 有多个 , 被观察的 主题对象 只有一个 ;

如 : 在购物网站 , 多个用户关注某商品后 , 当商品降价时 , 就会自动通知关注该商品的用户 ;

  • 主题对象 : 商品是主题对象 ;
  • 观察者 : 用户是观察者 ;
  • 观察者注册 : 用户关注 , 相当于注册观察者 ;
  • 通知触发条件 : 商品降价 ;

观察者模式 类型 : 行为型 ;





二、观察者模式适用场景



观察者模式适用场景 : 关联行为 场景 , 建立一套 触发机制 ;

如 : 用户关注某个商品的价格 , 降价时进行通知 , 这样 用户商品 产生了关联 , 触发机制就是 商品降价 ,





三、观察者模式优缺点



观察者模式 优点 :

  • 抽象耦合 :观察者被观察者 之间 , 建立了一个 抽象的 耦合 ; 由于 耦合 是抽象的 , 可以很容易 扩展 观察者 和 被观察者 ;
  • 广播通信 : 观察者模式 支持 广播通信 , 类似于消息广播 , 如果需要接收消息 , 只需要注册一下即可 ;

观察者模式 缺点 :

  • 依赖过多 : 观察者 之间 细节依赖 过多 , 会增加 时间消耗程序的复杂程度 ;
    这里的 细节依赖 指的是 触发机制 , 触发链条 ; 如果 观察者设置过多 , 每次触发都要花很长时间去处理通知 ;
  • 循环调用 : 避免 循环调用 , 观察者 与 被观察者 之间 绝对不允许循环依赖 , 否则会触发 二者 之间的循环调用 , 导致系统崩溃 ;




四、观察者模式代码示例



JDK 中提供了观察者模式的支持 ;

  • 被观察者 : 被观察者 继承 Observable 类 ;
  • 观察者 : 观察者 实现 Observer 接口 ;
  • 关联 观察者 与 被观察者 : 调用 被观察者 的 addObserver 方法 , 关联二者 ;
  • 触发通知 : 被观察者 数据改变时 , 调用 setChangednotifyObservers 方法 , 会自动回调 观察者的 update 方法 ;

用户在游戏中提出问题 , 管理员负责监听并处理问题 ;


1、被观察者


package observer;

import java.util.Observable;

/**
 * 被观察的主题对象
 *      JDK 提供了对观察者模式的支持 , 被观察者可以继承 Observable 类
 *
 * 被观察对象 继承 Observable 类
 */
public class Game extends Observable {
    private String name;

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

    public String getName() {
        return name;
    }

    /**
     * 用户提交问题
     * @param game
     * @param question
     */
    public void produceQuestion(Game game, Question question) {
        System.out.println(question.getUserName() +
                " 在 " + game.name + " 游戏中提交问题 : " + question.getContent());

        // 该方法是 Observable 提供的方法
        // 将 private boolean changed = false 成员变量设置为 true
        // 代表 被观察者 的状态发生了改变
        setChanged();

        // 通知 观察者
        notifyObservers(question);
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

2、观察者


package observer;

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

/**
 * 管理员类
 *      管理员类观察的是游戏
 *      用户反馈的问题 属于 游戏 , 管理员关注的是游戏
 *      无法关注 问题
 *
 * 如 : 在电商平台 , 关注的是某个商品 , 在降价时发送通知
 *      商品是存在的 , 降价消息 在关注的时候还没有被创建 , 是无法获取依赖的
 */
public class Manager implements Observer {
    /**
     * 管理员名称
     */
    private String name;

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

    @Override
    public void update(Observable o, Object arg) {
        // 获取 被观察者 对象
        Game game = (Game) o;

        // 获取 被观察者 对象 调用 notifyObservers 方法的参数
        Question question = (Question) arg;

        System.out.println(name + " 观察者 接收到 被观察者 " + game.getName() +
                " 的通知 , 用户 " + question.getUserName() +
                " 提出问题 " + question.getContent());
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

3、通知类


package observer;

public class Question {
    /**
     * 用户名
     */
    private String userName;

    /**
     * 问题内容
     */
    private String content;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

4、测试类


package observer;

public class Main {
    public static void main(String[] args) {
        // 创建被观察者
        Game game = new Game("Cat And Mouse");
        // 创建观察者
        Manager manager = new Manager("Tom");

        // 关联 观察者 与 被观察者
        game.addObserver(manager);

        // 业务逻辑 : 用户提出问题到游戏中 , 管理员接收到通知消息
        Question question = new Question();
        question.setUserName("Jerry");
        question.setContent("游戏崩溃");

        // 在游戏中提交问题
        game.produceQuestion(game, question);
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

执行结果 :

JerryCat And Mouse 游戏中提交问题 : 游戏崩溃
Tom 观察者 接收到 被观察者 Cat And Mouse 的通知 , 用户 Jerry 提出问题 游戏崩溃

  
 
  • 1
  • 2

在这里插入图片描述





五、JDK 中的观察者模式支持类




1、Observable


被观察者需要继承该类 ;

public class Observable {
    private boolean changed = false;
    /** 使用 Vector 是线程安全的集合 , 存放观察者对象 , 在构造器中初始化该类 */
    private Vector<Observer> obs;

    /** Construct an Observable with zero Observers. */

    public Observable() {
        obs = new Vector<>();
    }

    /**
     * 添加观察者对象 , 采用了 synchronized 修饰 , 是同步方法 , 保证了线程安全 
     *
     * @param   o   an observer to be added.
     * @throws NullPointerException   if the parameter o is null.
     */
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    /**
     * 删除观察者 
     */
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    /**
     * 通知观察者 , 无参
     *
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#hasChanged()
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    public void notifyObservers() {
        notifyObservers(null);
    }

    /**
     * 通知观察者 , 有参
     *
     * @param   arg   any object.
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#hasChanged()
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    public void notifyObservers(Object arg) {
        Object[] arrLocal;

        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    protected synchronized void setChanged() {
        changed = true;
    }

    protected synchronized void clearChanged() {
        changed = false;
    }

    public synchronized boolean hasChanged() {
        return changed;
    }

    /**
     * 查看观察者数量 
     *
     * @return  the number of observers of this object.
     */
    public synchronized int countObservers() {
        return obs.size();
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90

2、Observer


观察者需要实现该接口 ;

package java.util;

/**
 * A class can implement the <code>Observer</code> interface when it
 * wants to be informed of changes in observable objects.
 *
 * @author  Chris Warth
 * @see     java.util.Observable
 * @since   JDK1.0
 */
public interface Observer {
    /**
     * 当 被观察者 对象发生改变时 , 即被观察者对象调用 notifyObservers 方法时 , 自动调用该方法
     *
     * @param   o     被观察的对象.
     * @param   arg   被观察对象调用 notifyObservers 方法时传入的参数 
     */
    void update(Observable o, Object arg);
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

文章来源: hanshuliang.blog.csdn.net,作者:韩曙亮,版权归原作者所有,如需转载,请联系作者。

原文链接:hanshuliang.blog.csdn.net/article/details/119748365

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。