likes
comments
collection
share

从气象站到软件设计:深入解析观察者模式

作者站长头像
站长
· 阅读数 10

我们可能需要在某个对象的状态发生变化时,自动通知其他相关的对象更新自己的状态,但是我们又不希望直接在对象中硬编码这种通知机制,因为这样会导致代码的可维护性和可扩展性变差。

为了解决这种问题,我们可以使用观察者模式。观察者模式定义了一种松耦合的对象通信机制,使得多个对象可以在一起工作,而又不必互相了解。在观察者模式中,一个对象(称为主题)维护一组依赖于它的对象(称为观察者),并在状态发生变化时自动通知所有的观察者。这种机制可以使得代码更加灵活、可扩展和易于维护,从而提高软件的质量和效率。

因此,观察者模式被广泛应用于许多领域,包括桌面应用程序Web 应用程序移动应用程序等等。通过使用观察者模式,我们可以在不改变对象原有行为的情况下,动态地添加新的行为,从而使得代码更加灵活和易于维护。

什么是观察者模式

官方定义:

观察者模式:定义了一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会得到通知并自动更新。

The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

观察者模式的角色

  • Subject(主题):被观察的对象,维护一个观察者列表,并提供注册、删除和通知观察者的方法。

  • Observer(观察者):观察主题的对象,包含一个更新自己状态的方法。

  • ConcreteSubject(具体主题):实现主题接口,维护自己的状态并在状态发生变化时通知观察者。

  • ConcreteObserver(具体观察者):实现观察者接口,包含更新自己状态的具体实现。

观察者模式的实现

案例

想象一下,在一个城市里,有一个天气台多个手机App桌面App和网站,它们都需要显示当前的天气情况。在这种情况下,我们可以使用观察者模式来实现这个功能。 天气台(Subject)维护了当前的天气状况,并提供了一个方法来更新天气数据。手机App、桌面App和网站(Observers)订阅了天气台的天气数据,当天气数据发生变化时,天气台会自动通知所有的观察者,并更新它们自己的状态。

Subject(主题)

// 主题接口
interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

Subject(主题)类的方法比较固定,通常会包含以下三个方法:

  • registerObserver(Observer o):用于注册一个Observer对象,使得Subject能够向该Observer发送通知。
  • removeObserver(Observer o):用于移除一个已经注册的Observer对象。
  • notifyObservers():用于向所有已注册的Observer发送通知。

Observer(观察者)

// 观察者接口
interface Observer {
    void update(double temperature, double humidity, double pressure);
}
  • Observer(观察者)一般包含一个方法,即update()方法,该方法用于接收Subject发来的通知,并更新自己的状态。

ConcreteSubject(具体主题)

// 天气台类
class WeatherStation implements Subject {
    private double temperature;
    private double humidity;
    private double pressure;
    private List<Observer> observers = new ArrayList<>();

    public void setWeather(double temperature, double humidity, double pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        notifyObservers();
    }

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

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

    @Override
    public void notifyObservers() {
        for (Observer o : observers) {
            o.update(temperature, humidity, pressure);
        }
    }
}

ConcreteSubject(具体主题)通常会维护一个Observer列表,用于存储所有已经注册的Observer对象,以便在状态发生变化时能够通知它们。在具体实现中,

ConcreteSubject需要实现注册Observer、移除Observer和通知Observer等方法,并在状态发生变化时调用通知方法通知所有的Observer更新自己的状态。

WeatherStation就是一个具体的主题,它维护了一个List集合,用于存储所有已经注册的Observer对象,同时提供了注册Observer、移除Observer和通知Observer等方法。这种实现方式使得我们能够动态地添加或删除Observer对象,从而实现更好的灵活性和扩展性。

ConcreteObserver(具体观察者)

// 手机App、桌面App和网站类
class WeatherApp implements Observer {
    private String name;

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

    @Override
    public void update(double temperature, double humidity, double pressure) {
        System.out.printf("%s: 当前天气温度为 %.2f ℃,湿度为 %.2f%%,气压为 %.2f kPa\n", name, temperature, humidity, pressure);
    }
}

WeatherApp类实现了Observer接口,并重写了其中的update()方法。当WeatherStation的天气数据发生变化时,它会自动通知所有已经注册的Observer对象,并调用它们的update()方法。在WeatherAppupdate()方法中,它会获取WeatherStation的当前天气数据,并更新自己的界面显示。

WeatherApp类扮演的角色是具体的Observer,它实现了Observer接口,并在其中定义了具体的更新逻辑,即根据WeatherStation的天气数据更新自己的界面显示。同时,WeatherApp类还可以动态地注册、移除和更新自己的状态,以便更好地适应不同的应用场景和需求。

客户端

// 测试代码
public class ObserverPatternExample {
    public static void main(String[] args) {
        WeatherStation weatherStation = new WeatherStation();
        WeatherApp app1 = new WeatherApp("手机App1");
        WeatherApp app2 = new WeatherApp("桌面App1");
        WeatherApp app3 = new WeatherApp("网站1");

        weatherStation.registerObserver(app1);
        weatherStation.registerObserver(app2);
        weatherStation.registerObserver(app3);

        weatherStation.setWeather(20.5, 60.3, 101.3);
    }
}

观察者模式类图

维基百科

从气象站到软件设计:深入解析观察者模式

在spring源码中的应用

在Spring框架中,ApplicationContext中的事件、Bean生命周期事件和自定义事件都是观察者模式的具体应用。ApplicationContext作为主题(Subject)维护了一个监听器列表(Observer),用于存储所有已经注册的监听器。当事件发生时,ApplicationContext会自动通知所有的监听器,并调用它们的事件处理方法(onApplicationEvent方法),以便执行具体的逻辑。这种模式可以帮助我们在Spring应用程序中实现对象之间的动态通信,以便更好地满足业务需求和用户需求。

  1. ApplicationContext中的事件,例如ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent等;

  2. Bean生命周期事件,例如BeforeInitializationEvent、AfterInitializationEvent等;

  3. 自定义事件,例如订单创建事件、支付成功事件、邮件发送成功事件等。

这三个场景都是事件驱动模型,事件驱动模型是一种实现观察者模式的方式,它将观察者模式中的主题(Subject)和观察者(Observer)抽象为事件(Event)和监听器(Listener),并使用事件和监听器之间的关系来实现观察者模式。

代码举例

我来详细说一下自定义事件

举一个订单创建事件的例子。假设我们正在开发一个电商平台,我们需要在订单创建成功时,发送一封确认邮件给用户。为了实现这个功能,我们可以定义一个OrderCreatedEvent事件,并在订单创建时发布这个事件。具体代码如下:

首先,定义一个OrderCreatedEvent类,继承自ApplicationEvent类,用于表示订单创建事件:

public class OrderCreatedEvent extends ApplicationEvent {

    private Order order;

    public OrderCreatedEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }

    public Order getOrder() {
        return order;
    }
}

在订单创建成功时,我们可以通过ApplicationContext对象来发布一个OrderCreatedEvent事件,示例代码如下:

@Component
public class OrderService {

    @Autowired
    private ApplicationContext applicationContext;

    public void createOrder(Order order) {
        // 创建订单逻辑
        ...

        // 发布订单创建事件
        applicationContext.publishEvent(new OrderCreatedEvent(this, order));
    }
}

上述代码中,我们首先通过@Autowired注解注入了ApplicationContext对象,然后在订单创建成功后,通过applicationContext.publishEvent()方法来发布OrderCreatedEvent事件,并将订单信息封装到事件对象中。

接下来,我们需要定义一个监听器来监听OrderCreatedEvent事件,并在事件发生时发送确认邮件给用户。示例代码如下:

@Component
public class OrderCreatedEventListener implements ApplicationListener<OrderCreatedEvent> {

    @Override
    public void onApplicationEvent(OrderCreatedEvent event) {
        Order order = event.getOrder();
        // 发送确认邮件给用户
        ...
    }
}

在Spring框架中,自定义事件是一个非常重要的特性,它可以帮助我们实现各种复杂业务场景,例如订单创建事件、支付成功事件、邮件发送成功事件等。

举一个订单创建事件的例子。假设我们正在开发一个电商平台,我们需要在订单创建成功时,发送一封确认邮件给用户。为了实现这个功能,我们可以定义一个OrderCreatedEvent事件,并在订单创建时发布这个事件。具体代码如下:

首先,定义一个OrderCreatedEvent类,继承自ApplicationEvent类,用于表示订单创建事件:

javaCopy code
public class OrderCreatedEvent extends ApplicationEvent {

    private Order order;

    public OrderCreatedEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }

    public Order getOrder() {
        return order;
    }
}

在订单创建成功时,我们可以通过ApplicationContext对象来发布一个OrderCreatedEvent事件,示例代码如下:

javaCopy code
@Component
public class OrderService {

    @Autowired
    private ApplicationContext applicationContext;

    public void createOrder(Order order) {
        // 创建订单逻辑
        ...

        // 发布订单创建事件
        applicationContext.publishEvent(new OrderCreatedEvent(this, order));
    }
}

上述代码中,我们首先通过@Autowired注解注入了ApplicationContext对象,然后在订单创建成功后,通过applicationContext.publishEvent()方法来发布OrderCreatedEvent事件,并将订单信息封装到事件对象中。

接下来,我们需要定义一个监听器来监听OrderCreatedEvent事件,并在事件发生时发送确认邮件给用户。示例代码如下:

javaCopy code
@Component
public class OrderCreatedEventListener implements ApplicationListener<OrderCreatedEvent> {

    @Override
    public void onApplicationEvent(OrderCreatedEvent event) {
        Order order = event.getOrder();
        // 发送确认邮件给用户
        ...
    }
}

在上述代码中,我们定义了一个OrderCreatedEventListener监听器,实现了ApplicationListener接口,并重写了其中的onApplicationEvent()方法,用于接收OrderCreatedEvent事件的通知,并执行相应的逻辑,例如发送确认邮件给用户等。

通过这种方式,我们可以非常方便地实现订单创建事件的处理逻辑,并将处理逻辑与业务逻辑进行分离,从而使得代码更加模块化和易于维护。同时,我们还可以通过定义不同的自定义事件和监听器,来实现各种复杂的业务场景,例如支付成功事件、邮件发送成功事件等。

什么情况下使用观察者模式

  1. 当一个对象的改变需要同时改变其他多个对象的时候,使用观察者模式可以避免对象之间的紧密耦合。例如,在一个天气应用中,如果要在多个地方显示当前天气状况,我们可以使用观察者模式,将天气应用作为主题(Subject),将手机App、桌面App和网站作为观察者(Observer),以便在天气数据发生变化时,自动更新所有的观察者。

  2. 当一个对象的改变会触发连锁反应,引起其他多个对象的状态变化时,使用观察者模式可以帮助我们更好地管理这种连锁反应。例如,在一个电商应用中,如果用户下单成功后,需要触发一系列的事件,例如减少库存、生成订单、发送短信等,我们可以使用观察者模式,将订单作为主题(Subject),将库存管理器、订单管理器、短信发送器等作为观察者(Observer),以便在订单创建成功后,自动触发这些事件。

  3. 当一个对象的改变需要动态地添加或删除其他多个对象时,使用观察者模式可以帮助我们更好地管理这些动态变化。例如,在一个社交应用中,如果用户在发布一条动态时,可以选择将这条动态发布到某个圈子或者某些好友,我们可以使用观察者模式,将用户作为主题(Subject),将圈子和好友作为观察者(Observer),以便在用户选择发布圈子或好友时,动态地添加或删除对应的观察者。

总结:

观察者模式的优点:

  1. 解耦性好:观察者模式可以将主题(Subject)和观察者(Observer)解耦,从而使得它们之间的依赖关系变得松散。这样一来,主题和观察者就可以相互独立地变化,而不会对彼此造成影响。

  2. 可扩展性好:观察者模式可以很容易地添加新的观察者,从而实现更加灵活的扩展。这样一来,当新的观察者加入时,主题和已有的观察者不需要做任何修改,只需要添加新的观察者即可。

  3. 便于维护:观察者模式可以将逻辑分离,从而使得代码更加清晰、简洁和易于维护。这样一来,当需求变化时,我们只需要修改观察者的实现,而不需要修改主题的实现,从而避免了代码的耦合性。

  4. 易于实现:观察者模式的实现非常简单,只需要定义好主题和观察者的接口,并在主题中维护一个观察者列表,即可实现观察者模式的功能。

观察者模式的缺点:

  1. 过多的观察者会导致性能问题:当主题有过多的观察者时,会导致通知观察者的时间延长,从而影响性能。因此,在使用观察者模式时,需要注意观察者的数量,避免过多的观察者。

  2. 观察者和主题之间的关系可能难以理解:当观察者和主题之间的关系过于复杂时,可能会导致代码难以理解和维护。因此,在使用观察者模式时,需要注意代码的清晰度和简洁度。

总之,观察者模式是一种非常常用的设计模式,它可以帮助我们实现对象之间的松耦合,并使得代码更加灵活、可扩展和易于维护。虽然观察者模式存在一些缺点,但只要合理使用和设计,就可以最大化地发挥其优点,从而满足各种业务需求。

转载自:https://juejin.cn/post/7222910781922623525
评论
请登录