30 设计模式:观察者模式-行为型模式

98 阅读7分钟

今天发现微信被拉黑了,哈哈哈哈哈哈。除了代码一无所有

行为型模式之:

  • 职责链模式(Chain of Responsibility)
    重要程度:⭐️⭐️⭐️

  • 命令模式(Command)
    重要程度:⭐️⭐️⭐️⭐️

  • 解释器模式(Interpreter)
    重要程度:⭐️

  • 迭代器模式(Iterator)
    重要程度:⭐️⭐️⭐️⭐️⭐️

  • 中介者模式(Mediator)
    重要程度:⭐️⭐️

  • 备忘录模式(Memento)
    重要程度:⭐️⭐️

  • 观察者模式(Observer)
    重要程度:⭐️⭐️⭐️⭐️⭐️

  • 状态模式(State)
    重要程度:⭐️⭐️⭐️

  • 策略模式(Strategy)
    重要程度:⭐️⭐️⭐️⭐️

  • 模板方法模式(Template Method)
    重要程度:⭐️⭐️⭐️

  • 访问者模式(Visitor)
    重要程度:⭐️

前面学习了解决“对象的创建”的创建型设计模式,解决“类或对象的组合或组装”结构型设计模式,今天学习最后一类设计模式,解决“类或对象之间的交互”问题的行为型设计模式。

从上面的列举中可以看出,最重要的或者说最常使用的设计模式主要是观察者模式、迭代器模式;命令模式和策略模式次之,职责链模式、状态模式和模版模式位于最后。

今天主要学习观察者模式,我一个同事说,设计模式他主要是使用的就是观察者模式。特别是eventBus就是观察者模式的主要体现和常用框架。

1.什么是观察者模式

观察者模式也叫订阅者模式。 它定义了一种一对多的关系,当一个对象的状态发生变化时,所有依赖它的对象都会得到通知并自动更新。 顾名思义和我们订阅报纸一样,一个小区有100户人家,只有1家订阅了报纸,送报员每天早晨都会送到,没有定报纸的99户人家不会受到任何影响。 举一个简单的例子来说明观察者模式:

假设有一个天气数据系统,用户可以订阅天气数据并随时获取最新的天气信息。

  1. 观察者(Observer):用户就是观察者,他们希望在天气数据更新时得到通知并获取最新的天气信息。
  2. 被观察者(Subject):天气数据系统是被观察者,它负责收集和更新天气信息,并在数据更新时通知所有的观察者。

image.png

注意。观察者模式是通知想要获取到信息的用户,不想获取信息的用户是没有必要去实现的,提取下观察者模式的思想:

  • 1:想要获取的用户,首先要成为订阅者。
  • 2:订阅想要获取的内容。
  • 3:移除不想订阅的内容(订阅者5)

2.观察者模式的实现

// 观察者接口
interface Observer {
    void update(String weather);
}

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

// 具体的观察者实现类
class User implements Observer {
    private String name;

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

    @Override
    public void update(String weather) {
        System.out.println(name + "收到最新的天气信息:" + weather);
    }
}

// 具体的被观察者实现类
class WeatherData implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String weather;

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

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

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

    // 当天气更新时调用该方法
    public void setWeather(String weather) {
        this.weather = weather;
        notifyObservers(); // 通知所有观察者
    }
}

// 测试代码
public class Main {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        
        // 创建观察者并注册到被观察者中
        Observer user1 = new User("User1");
        Observer user2 = new User("User2");
        weatherData.registerObserver(user1);
        weatherData.registerObserver(user2);

        // 模拟天气更新
        weatherData.setWeather("晴天");
        weatherData.setWeather("阴天");
    }
}

在上面的示例中,User类是观察者,它实现了Observer接口;WeatherData类是被观察者,它实现了Subject接口。当WeatherData的天气更新时,会通知所有注册的观察者,观察者会收到最新的天气信息并做出相应的处理。

3.观察者模式的选择场景

想要监听获取对象的状态变化&&通知对象我方的状态变化

  1. 当一个对象的状态发生变化需要通知其他对象,并且不确定这些对象是谁。
  2. 当一个对象需要在改变时通知其他对象,但又不希望这些对象是紧密耦合的。
  3. 当一个对象的改变需要其他对象采取一系列的动作,但不清楚有多少对象需要采取这些动作。
  4. 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这两个方面分离开来可能会更好。

4.观察者模式的实际使用

  1. Android系统中的LiveData: LiveData是一种可观察的数据持有类,在数据发生变化时通知观察者。它常用于在Android应用程序中管理UI组件的数据,并确保UI组件在数据变化时及时更新。
  2. Java Swing和JavaFX中的事件处理机制: 在Java Swing和JavaFX中,观察者模式用于处理用户界面事件。事件源(如按钮、文本框等)充当被观察者,而事件监听器则充当观察者,监听器会在事件发生时接收通知并执行相应的操作。
  3. RxJava: RxJava是一个在Java平台上实现的响应式编程库,它使用观察者模式来支持响应式编程范式。在RxJava中,被观察者(Observable)可以发出事件,而观察者(Observer)则订阅这些事件,并在事件发生时做出响应。
  4. Spring Framework中的事件机制: Spring框架提供了一个事件机制,允许应用程序内的组件通过发布事件来通知其他组件。事件源是应用程序中的任何对象,它可以发布事件并将其传递给已注册的监听器(观察者)。

当然,这里以eventBus为例。

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class EventBus {
    private static EventBus instance;
    private Map<Class<?>, List<Subscriber>> subscribers;

    private EventBus() {
        subscribers = new HashMap<>();
    }

    public static EventBus getInstance() {
        if (instance == null) {
            synchronized (EventBus.class) {
                if (instance == null) {
                    instance = new EventBus();
                }
            }
        }
        return instance;
    }

    public void register(Object subscriber) {
        for (Map.Entry<Class<?>, List<Subscriber>> entry : subscribers.entrySet()) {
            if (entry.getKey().isAssignableFrom(subscriber.getClass())) {
                entry.getValue().add(new Subscriber(subscriber));
            }
        }
    }

    public void post(Object event) {
        Class<?> eventClass = event.getClass();
        for (Map.Entry<Class<?>, List<Subscriber>> entry : subscribers.entrySet()) {
            if (entry.getKey().isAssignableFrom(eventClass)) {
                for (Subscriber subscriber : entry.getValue()) {
                    subscriber.handleEvent(event);
                }
            }
        }
    }

    public void unregister(Object subscriber) {
        for (List<Subscriber> subscriberList : subscribers.values()) {
            for (Subscriber sub : subscriberList) {
                if (sub.getSubscriber() == subscriber) {
                    subscriberList.remove(sub);
                    break;
                }
            }
        }
    }

    private static class Subscriber {
        private Object subscriber;

        public Subscriber(Object subscriber) {
            this.subscriber = subscriber;
        }

        public Object getSubscriber() {
            return subscriber;
        }

        public void handleEvent(Object event) {
            // Handle the event
        }
    }
}
  • EventBus是单例模式,通过getInstance()方法获取实例。
  • register(Object subscriber)方法用于注册订阅者,将订阅者与相应的事件类型关联起来。
  • post(Object event)方法用于发布事件,当有事件发生时,EventBus会将事件传递给所有订阅者。
  • unregister(Object subscriber)方法用于取消注册订阅者,避免内存泄漏。

在实际的EventBus库中,还包括了更多功能,如事件的线程调度、粘性事件支持等。以上代码仅展示了EventBus的基本原理和简化实现。EventBus详细的文章可以看这个链接,很细juejin.cn/post/684490…

5.观察者模式的优缺点

优点:

  • 开闭原则。 你无需修改发布者代码就能引入新的订阅者类 (如果是发布者接口则可轻松引入发布者类)。
  • 你可以在运行时建立对象之间的联系

缺点:

  1. 引起内存泄漏: 如果观察者没有被正确地取消注册,就有可能引起内存泄漏问题,因为被观察者保留了对观察者的引用。
  2. 通知顺序不确定: 观察者模式中,观察者的调用顺序是不确定的,可能会导致意外的结果,需要在设计上进行合理的考虑和规划。
  3. 可能导致性能问题: 如果被观察者对象有大量的观察者,并且频繁地状态变化,可能会导致性能问题,因为每次状态变化都需要通知所有的观察者。