设计模式 - 观察者模式

114 阅读4分钟

设计模式 - 观察者模式

一、引入

观察者模式就好像是你订阅了一个邮件列表,当邮件列表中有新的消息时,你会收到通知。在软件设计中,观察者模式允许一个对象(称为“主题”或“被观察者”)维护一组依赖它的对象(称为“观察者”),并在状态变化时通知所有观察者。

举个例子,想象你在一个新闻网站上订阅了一些新闻类别,比如体育、科技、娱乐等。当有新的新闻发布时,你会收到相应类别的通知。这里,你就是观察者,新闻网站就是被观察者。

二、概念

观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象。当主题对象的状态发生变化时,所有依赖于它的观察者都会得到通知并自动更新。

三、基本结构

observer_pattern_uml_diagram.jpg

  1. Subject(主题)
    • 定义了一个抽象接口,用于添加、删除和通知观察者对象。
    • 具体主题(ConcreteSubject)实现了主题接口,维护了一个观察者列表,并在状态发生变化时通知观察者。
  2. Observer(观察者)
    • 定义了一个更新的接口,用于接收主题的通知并进行相应的处理。
    • 具体观察者(ConcreteObserver)实现了观察者接口,在接收到通知时执行相应的操作。

四、示例代码

主题相关:

/**
 * 主题接口 : 也称为被观察者或可观察者,它是一个具有状态的对象,当状态发生变化时会通知所有的观察者。
 */
public interface Subject {
    /**
     * 注册观察者
     *
     * @param observer
     */
    void registerObserver(Observer observer);

    /**
     * 删除观察者
     *
     * @param observer
     */
    void removeObserver(Observer observer);

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

/**
 * 具体主题对象
 */
public class ConcreteSubject implements Subject {

    private List<Observer> observers = new ArrayList<>();

    private String message;

    /**
     * 注册观察者
     *
     * @param observer
     */
    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    /**
     * 删除观察者
     *
     * @param observer
     */
    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    /**
     * 通知观察者
     */
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }

    public void setMessage(String message) {
        this.message = message;
        notifyObservers();
    }
}


观察者:

/**
 * 观察者接口 : 定义了一个更新方法,用于在接收到主题的通知时更新自己的状态。
 */
public interface Observer {
    /**
     * 更新数据
     *
     * @param message
     */
    void update(String message);
}

/**
 * 观察者A
 */
public class ConcreteObserverA implements Observer {
    @Override
    public void update(String message) {
        System.out.println("观察者A收到消息: " + message);
    }
}

/**
 * 观察者B
 */
public class ConcreteObserverB implements Observer{
    @Override
    public void update(String message) {
        System.out.println("观察者B收到消息:" + message);
    }
}

客户端:

public class Client {
    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();

        //观察者a
        Observer observerA = new ConcreteObserverA();
        //观察者b
        Observer observerB = new ConcreteObserverB();

        //注册到主题
        subject.registerObserver(observerA);
        subject.registerObserver(observerB);
      
        subject.setMessage("5435");
    }
}

五、能干什么

  1. 对象间的松耦合:观察者模式可以降低对象之间的直接依赖关系,使得主题和观察者之间相互独立,可以单独变化,而不会影响对方。
  2. 一对多的通信:观察者模式支持一对多的通信机制,一个主题可以同时通知多个观察者,适用于需要广播通知的场景。
  3. 事件处理:当一个事件发生时,可以通过观察者模式来通知所有关注该事件的观察者,实现事件的处理和响应。
  4. 消息通知:例如,一个新闻网站发布了一篇新的文章,需要通知所有订阅了该类别的用户。
  5. 模块解耦:通过观察者模式,可以将一个模块拆分成两个独立的部分,一个负责产生事件,另一个负责处理事件,从而实现模块之间的解耦。
  6. 实现广播机制:例如,一个股票价格变动时,需要通知所有关注该股票的投资者。

六、总结

优点:

  1. 松耦合(Loose Coupling):主题和观察者之间的关系是松耦合的,它们不直接依赖于彼此的具体实现。这使得可以动态地添加、移除观察者,而不影响主题和观察者之间的交互。
  2. 支持广播通信:当主题状态发生变化时,所有注册的观察者都会收到通知。这种一对多的通信机制非常适合事件处理、消息通知等场景。
  3. 扩展性强:可以轻易地添加新的观察者,扩展系统功能。
  4. 降低对象间的直接依赖关系:主题和观察者之间只依赖于抽象接口,不依赖于具体实现,使得系统更灵活、可维护。

缺点:

  1. 可能导致内存泄漏:如果观察者没有被正确地移除,可能会导致内存泄漏。
  2. 通知顺序不确定:由于观察者是异步执行的,所以观察者接收通知的顺序是不确定的。
  3. 可能引起性能问题:如果通知操作涉及复杂的逻辑或者大量的观察者,可能会影响系统的性能。
  4. 增加调试难度:由于观察者模式将系统分散为许多小对象,因此在调试时可能需要跟踪多个对象之间的交互。