揭秘设计模式:从UI按钮到Spring事件的观察者模式

揭秘设计模式:从UI按钮到Spring事件的观察者模式

设计模式是软件开发的基石,它们是解决常见问题的最佳实践。今天,我们将深入探讨一个既简单又强大的行为型模式:观察者模式(Observer Pattern)

如果你用过前端框架,或者写过任何带UI界面的程序,你可能已经在不经意中运用过它了。

1. 什么是观察者模式?

想象一下你订阅了一份报纸。一旦报社有新的报纸出版(状态改变),他们就会自动把报纸送到你家。你不需要每天打电话去问"今天有新报纸吗?"。

这就是观察者模式的核心思想:定义对象间的一种一对多的依赖关系,当一个对象(被观察者)的状态发生改变时,所有依赖于它的对象(观察者)都会得到通知并自动更新。

这个模式实现了对象间的松散耦合。被观察者只负责通知,它无需关心谁在监听,也不用知道监听者会做什么。这让系统变得更灵活,更容易扩展。

2. 观察者模式的三个核心角色

为了实现这个模式,我们需要三个核心组件:

  • 观察者(Observer) :它定义了一个接口,所有"订阅者"都必须实现这个接口。这个接口通常只有一个方法,比如 update(),用于接收被观察者的通知。

  • 被观察者(Subject) :它管理着一个观察者列表。它提供了订阅attach())、取消订阅detach())和通知notify())三个方法。当它的状态发生变化时,它会调用 notify() 方法来通知所有注册的观察者。

  • 具体角色(Concrete Implementations) :这是观察者和被观察者的具体实现类。例如,一个 Button 类可以作为具体的被观察者,而一个 TextDisplay 类则可以作为具体的观察者。

3. 观察者模式的UML类图

理解了核心概念,让我们通过UML类图来直观地看一下它们的关系:

从图中可以看出,ConcreteSubject 只需要依赖 Observer 接口,而不需要知道具体的 ConcreteObserver 类。这就是面向接口编程的体现,它有效地降低了代码耦合。

4. Java代码实现

现在,我们用 Java 代码将这个模式实现出来,以便更好地理解各个组件是如何协作的。

1. 观察者接口 (Observer)

定义一个更新方法,当被观察者状态改变时被调用

java 复制代码
import java.util.ArrayList;
import java.util.List;

interface Observer {
    void update(String message);
}
2. 被观察者接口 (Subject)

定义了管理和通知观察者的方法

java 复制代码
interface Subject {
    void attach(Observer observer);
    void detach(Observer observer);
    void notifyObservers(String message);
}
3. 具体观察者 A (ConcreteObserver)

实现了观察者接口,当收到通知时执行自己的逻辑

java 复制代码
class ConcreteObserverA implements Observer {
    private String name;

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

    @Override
    public void update(String message) {
        System.out.println(name + " 收到通知: " + message);
        // 执行自己的业务逻辑,例如更新UI
    }
}
4. 具体观察者 B (ConcreteObserver)
java 复制代码
class ConcreteObserverB implements Observer {
    private String name;

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

    @Override
    public void update(String message) {
        System.out.println(name + " 收到通知: " + message);
        // 执行自己的业务逻辑,例如记录日志
    }
}
5. 具体的被观察者 (ConcreteSubject)

实现了被观察者接口,维护观察者列表

java 复制代码
class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String subjectState;

    // 添加观察者到列表
    @Override
    public void attach(Observer observer) {
        observers.add(observer);
        System.out.println("成功订阅: " + ((ConcreteObserverA) observer).name);
    }

    // 从列表移除观察者
    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
        System.out.println("取消订阅: " + ((ConcreteObserverA) observer).name);
    }

    // 通知所有观察者
    @Override
    public void notifyObservers(String message) {
        System.out.println("被观察者状态改变,正在通知所有观察者...");
        for (Observer observer : observers) {
            observer.update(message);
        }
    }

    // 改变被观察者的状态并通知观察者
    public void changeState(String newState) {
        this.subjectState = newState;
        System.out.println("被观察者状态已改变为: " + newState);
        notifyObservers("状态已更新为: " + newState);
    }
}
6. 主程序
java 复制代码
public class ObserverPatternDemo {
    public static void main(String[] args) {
        // 创建被观察者对象
        ConcreteSubject subject = new ConcreteSubject();

        // 创建两个观察者对象
        ConcreteObserverA observer1 = new ConcreteObserverA("观察者 A");
        ConcreteObserverB observer2 = new ConcreteObserverB("观察者 B");

        // 观察者进行订阅
        subject.attach(observer1);
        subject.attach(observer2);

        System.out.println("--------------------");

        // 改变被观察者状态,所有观察者都会收到通知
        subject.changeState("订单已创建");

        System.out.println("--------------------");

        // 观察者 A 取消订阅
        subject.detach(observer1);

        System.out.println("--------------------");

        // 再次改变被观察者状态,只有观察者 B 会收到通知
        subject.changeState("订单已发货");
    }
}

5. 从理论到实践:UI事件与业务开发

理解了观察者模式的原理,我们再来看看它如何在日常业务开发中发挥作用。最常见的应用场景,就是我们刚才讨论的 UI 事件,以及更通用的业务逻辑解耦。

UI事件监听:按钮与业务解耦

我们以一个最常见的例子来串联整个流程:UI按钮的点击事件

在传统开发中,你可能会在按钮的点击回调里直接编写业务逻辑。例如,一个"注册"按钮的点击事件,可能会直接调用 UserService.register()NotificationService.sendWelcomeEmail() 等方法。这样做会导致按钮和业务逻辑紧密耦合。

使用观察者模式,我们可以这么做:

  1. 被观察者(Button :我们的按钮类可以被设计成一个具体的被观察者。它内部维护一个观察者列表,并提供 attach()detach() 方法。

  2. 观察者(RegisterController :我们的业务逻辑类,比如 RegisterController,会实现一个 IObserver 接口,并重写 update() 方法。

  3. 订阅事件 :在程序初始化时,我们创建 ButtonRegisterController 实例,然后调用 button.attach(registerController),将 RegisterController 注册为 Button 的观察者。

  4. 触发通知 :当用户点击按钮时,Button 内部的 onClick() 方法被调用,它会随即执行 notify() 方法。

  5. 自动响应notify() 方法遍历观察者列表,找到 RegisterController 并调用它的 update() 方法。此时,RegisterController 才开始执行注册的业务逻辑。

这个流程让 UI 逻辑和业务逻辑彻底分离。Button 只负责"被点击"这个事实,它完全不关心点击之后会发生什么。而 RegisterController 则负责响应这个事件 。这使得UI层的变动不会影响业务逻辑,反之亦然。

业务事件解耦:订单与通知

在更复杂的业务系统中,观察者模式同样大放异彩。考虑一个电商平台,当用户成功下单后,我们需要做几件事:发送确认邮件、更新用户积分、记录操作日志。

如果在一个方法里依次调用这三个服务,将来如果新增一个功能(比如发送短信),我们必须修改这个方法。这导致了代码的脆弱和难以维护。

通过引入业务事件,我们就能完美解决这个问题:

  1. 定义事件 :创建一个 OrderCreatedEvent 类,作为我们的业务事件对象。

  2. 发布事件 :在处理订单的服务中,当订单创建成功后,我们只发布一个 OrderCreatedEvent。这个服务不需要知道后续的邮件或积分逻辑。

  3. 监听事件 :邮件服务、积分服务和日志服务分别作为独立的"观察者",监听 OrderCreatedEvent。当事件发生时,它们会自动被触发,执行各自的逻辑。

这种模式让你的代码更加面向事件 而非面向过程 。新的业务功能可以轻松添加为新的观察者,而无需修改任何核心代码,这正是开闭原则(Open/Closed Principle)的体现。


6. 框架中的实际应用:Spring事件机制

观察者模式在现代软件框架中得到了广泛应用。在 Spring 框架 里,它的思想被巧妙地融入了事件驱动机制

  • 被观察者 :Spring 的 ApplicationContext 充当了被观察者的角色。

  • 观察者 :你的业务服务(Service)或组件(Component),通过实现 ApplicationListener 接口或使用 @EventListener 注解来监听事件。

让我们以电商平台为例,看看它在 Spring 中的实现。首先,我们需要定义事件本身:

java 复制代码
import org.springframework.context.ApplicationEvent;

/**
 * 这是一个业务事件类,代表"订单已创建"这个事件。
 * 我们可以将事件发生时的数据(例如订单对象)封装在这个类中。
 */
public class OrderCreatedEvent extends ApplicationEvent {
    private final Order order;

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

    public Order getOrder() {
        return order;
    }
}

接着,我们的业务代码就可以发布和监听这个事件了:

java 复制代码
// 2. 发布事件
@Autowired
private ApplicationEventPublisher eventPublisher;

public void createOrder(Order order) {
    // 订单创建的业务逻辑...
    eventPublisher.publishEvent(new OrderCreatedEvent(this, order));
}

// 3. 监听事件 (观察者)
@Component
public class EmailService {
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        // 发送邮件的逻辑...
    }
}

通过这种方式,订单服务完全不知道谁在监听它的事件。如果将来需要添加新的功能,比如短信通知或库存同步,我们只需要编写一个新的监听器,而无需修改任何现有代码。这种开闭原则的体现,正是观察者模式的精髓所在。

7. 总结

观察者模式是一个优雅且强大的工具,它通过解耦对象间的依赖,让我们的代码更灵活、更易于维护和扩展。无论是简单的UI事件,还是复杂的分布式消息队列,它的思想都无处不在。掌握了这个模式,你就掌握了编写可维护、可扩展代码的关键。

相关推荐
liang_jy7 小时前
责任链模式
android·设计模式·面试
现在没有牛仔了7 小时前
用Spring Boot+工厂+策略模式优雅解耦支付系统
java·后端·设计模式
钢铁男儿7 小时前
C# 一个投资跟踪程序的设计与实现:面向对象与设计模式的深度解析
java·设计模式·c#
小Lu的开源日常1 天前
为什么计算机用“补码”存储整数?
设计模式·面试·计算机组成原理
蒋星熠1 天前
Python API接口实战指南:从入门到精通
开发语言·分布式·python·设计模式·云原生·性能优化·云计算
努力也学不会java2 天前
【设计模式】简单工厂模式
java·开发语言·设计模式·简单工厂模式
Leo来编程2 天前
设计模式8-命令模式
设计模式·命令模式
易元2 天前
模式组合应用-组合模式
后端·设计模式
秋难降2 天前
从浅克隆到深克隆:原型模式如何解决对象创建的 “老大难”?😘
后端·设计模式·程序员