【软件设计模式】 观察者模式详解

观察者模式

观察者模式是一种行为设计模式,它定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

这种模式特别适合于实现分布式系统中的事件处理系统,以及在需要在应用中实现广播通信时使用。

组成

  • 发布者(Subject)
    会向观察者发送值得关注的事件。 事件会在发布者自身状态改变或执行特定行为后发生。 发布者维护了一个订阅列表,并提供了添加、删除和通知观察者的方法。
    当新事件发生时,发布者会遍历订阅列表并调用每个订阅者对象的通知方法。 该方法是在订阅者接口中声明的。
  • 观察者(Observer)
    观察者是一个依赖于发布者状态的对象,它会在被观察者状态发生变化时接收到通知并作出相应的处理。观察者定义了一个update()方法,该方法可以拥有多个参数, 使发布者能在更新时传递事件的详细信息,用于在接收到通知后更新自身状态或执行相应的操作。

在这里插入图片描述

适用场景

  • 当一个对象状态的改变需要改变其他对象, 或实际对象是事先未知的或动态变化的时, 可使用观察者模式。

    当你使用图形用户界面类时通常会遇到一个问题。 比如,你创建了自定义按钮类并允许客户端在按钮中注入自定义代码, 这样当用户按下按钮时就会触发这些代码。观察者模式允许任何实现了订阅者接口的对象订阅发布者对象的事件通知。 你可在按钮中添加订阅机制, 允许客户端通过自定义订阅类注入自定义代码。

  • 当应用中的一些对象必须观察其他对象时,可使用该模式。

    但仅能在有限时间内或特定情况下使用。订阅列表是动态的, 因此订阅者可随时加入或离开该列表。

实现方式

  1. 仔细检查你的业务逻辑,试着将其拆分为两个部分: 独立于其他代码的核心功能将作为发布者; 其他代码则将转化为一组订阅类。

  2. 声明订阅者接口。 该接口至少应声明一个update()方法。

  3. 声明发布者接口并定义一些接口来在列表中添加和删除订阅对象。 记住发布者必须仅通过订阅者接口与它们进行交互。

  4. 确定存放实际订阅列表的位置并实现订阅方法。 通常所有类型的发布者代码看上去都一样, 因此将列表放置在直接扩展自发布者接口的抽象类中是显而易见的。 具体发布者会扩展该类从而继承所有的订阅行为。
    但是, 如果你需要在现有的类层次结构中应用该模式, 则可以考虑使用组合的方式: 将订阅逻辑放入一个独立的对象, 然后让所有实际订阅者使用该对象。

  5. 创建具体发布者类。 每次发布者发生了重要事件时都必须通知所有的订阅者。

  6. 在具体订阅者类中实现通知更新的方法。 绝大部分订阅者需要一些与事件相关的上下文数据。 这些数据可作为通知方法的参数来传递。

    但还有另一种选择。 订阅者接收到通知后直接从通知中获取所有数据。 在这种情况下, 发布者必须通过更新方法将自身传递出去。 另一种不太灵活的方式是通过构造函数将发布者与订阅者永久性地连接起来。

  7. 客户端必须生成所需的全部订阅者, 并在相应的发布者处完成注册工作。

意义

  • 观察者模式实现了被观察者和观察者之间的松耦合,被观察者并不知道具体有哪些观察者,而观察者也不知道被观察者的具体细节,它们只是通过接口进行通信。
  • 被观察者在状态发生变化时会通知所有的观察者,观察者接收到通知后可以执行相应的操作。
  • 观察者模式实现了一对多的依赖关系,一个被观察者可以有多个观察者,并且这些观察者可以同时监听被观察者的状态变化。
  • 观察者模式允许动态地添加和删除观察者,被观察者并不依赖于具体的观察者对象,从而实现了更大的灵活性。
  • 你无需修改发布者代码就能引入新的订阅者类 (如果是发布者接口则可轻松引入发布者类)。

软件设计原则:开闭原则好莱坞原则

示例

假设我们在开发一个游戏,游戏中有一个英雄(Hero)角色,当英雄的生命值发生变化时,需要通知游戏界面(UI)更新显示的生命值,同时也要通知成就系统(AchievementSystem)检查是否触发了某些成就。

// 观察者接口
interface Observer {
    void update(int health);
}

// 被观察者接口
interface Subject {
    void attach(Observer observer); // registerObserver
    void detach(Observer observer); // removeObserver
    void notifyObservers();
}

class Hero implements Subject {
    private int health = 100;
    private List<Observer> observers = new ArrayList<>();

    public void setHealth(int health) {
        this.health = health;
        notifyObservers();
    }

    public int getHealth() {
        return health;
    }

    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(this.health);
        }
    }
}

class GameUI implements Observer {
    @Override
    public void update(int health) {
        System.out.println("UI: Hero health updated to " + health);
    }
}

class AchievementSystem implements Observer {
    @Override
    public void update(int health) {
        if (health == 100) {
            System.out.println("Achievement unlocked: Full Health");
        }
    }
}

public class ObserverDemo {
    public static void main(String[] args) {
        Hero hero = new Hero();
        GameUI ui = new GameUI();
        AchievementSystem achievementSystem = new AchievementSystem();

        // 注册观察者
        hero.attach(ui);
        hero.attach(achievementSystem);

        // 改变英雄的生命值,观察者会被通知
        hero.setHealth(90);
        hero.setHealth(100); // 这将触发成就系统的通知
    }                           
}

观察者模式就像是订阅了某个主题(如新闻、博客),当有新内容时,所有订阅者都会收到通知。在我们的游戏例子中,Hero就像是一个新闻发布者,而GameUI和AchievementSystem就像是订阅了这个新闻的读者,当新闻(即英雄的生命值变化)发布时,所有读者都会得到更新。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值