观察者模式深度解析:实现松耦合的订阅-发布系统

2025/10/10 Design Patterns 共 6742 字,约 20 分钟

观察者模式深度解析:实现松耦合的订阅-发布系统

在软件开发中,我们经常遇到这样的场景:当一个对象的状态发生变化时,需要自动通知其他对象,并且这些被通知的对象可能是不确定的。观察者模式(Observer Pattern)正是为解决这类问题而生的经典设计模式。

什么是观察者模式?

观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,它会通知所有观察者对象,使它们能够自动更新。

核心角色

  • Subject(主题):被观察的对象,维护观察者列表,提供注册和删除观察者的方法
  • Observer(观察者):定义更新接口,用于在主题状态改变时接收通知
  • ConcreteSubject(具体主题):具体被观察对象,状态改变时通知所有观察者
  • ConcreteObserver(具体观察者):实现观察者接口,维护对具体主题的引用

观察者模式的实现方式

1. 推模型 vs 拉模型

观察者模式有两种主要的实现方式:

推模型:主题主动将详细的变化数据推送给观察者 拉模型:主题只通知观察者状态发生变化,观察者主动从主题拉取所需数据

2. 经典实现示例

下面我们通过一个新闻发布系统的例子来展示观察者模式的实现:

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

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

// 具体主题 - 新闻发布器
class NewsPublisher implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String latestNews;
    
    @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(latestNews);
        }
    }
    
    public void publishNews(String news) {
        this.latestNews = news;
        System.out.println("发布新闻: " + news);
        notifyObservers();
    }
}

// 具体观察者 - 邮件订阅者
class EmailSubscriber implements Observer {
    private String name;
    
    public EmailSubscriber(String name) {
        this.name = name;
    }
    
    @Override
    public void update(String news) {
        System.out.println(name + " 收到邮件通知: " + news);
    }
}

// 具体观察者 - 短信订阅者
class SMSSubscriber implements Observer {
    private String phone;
    
    public SMSSubscriber(String phone) {
        this.phone = phone;
    }
    
    @Override
    public void update(String news) {
        System.out.println("向 " + phone + " 发送短信: " + news);
    }
}

// 使用示例
public class ObserverPatternDemo {
    public static void main(String[] args) {
        NewsPublisher publisher = new NewsPublisher();
        
        Observer emailSubscriber1 = new EmailSubscriber("张三");
        Observer emailSubscriber2 = new EmailSubscriber("李四");
        Observer smsSubscriber = new SMSSubscriber("13800138000");
        
        publisher.registerObserver(emailSubscriber1);
        publisher.registerObserver(emailSubscriber2);
        publisher.registerObserver(smsSubscriber);
        
        publisher.publishNews("Java 21 正式发布!");
        
        // 输出:
        // 发布新闻: Java 21 正式发布!
        // 张三 收到邮件通知: Java 21 正式发布!
        // 李四 收到邮件通知: Java 21 正式发布!
        // 向 13800138000 发送短信: Java 21 正式发布!
    }
}

现代实现:使用Java内置观察者

Java在java.util包中提供了内置的观察者支持:

import java.util.Observable;
import java.util.Observer;

// 使用Java内置Observable
class StockMarket extends Observable {
    private String stockSymbol;
    private double price;
    
    public StockMarket(String stockSymbol, double price) {
        this.stockSymbol = stockSymbol;
        this.price = price;
    }
    
    public void setPrice(double price) {
        if (this.price != price) {
            this.price = price;
            setChanged(); // 标记状态已改变
            notifyObservers(price); // 通知观察者,传递价格数据
        }
    }
    
    public double getPrice() {
        return price;
    }
    
    public String getStockSymbol() {
        return stockSymbol;
    }
}

// 使用Java内置Observer
class StockTrader implements Observer {
    private String name;
    
    public StockTrader(String name) {
        this.name = name;
    }
    
    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof StockMarket) {
            StockMarket market = (StockMarket) o;
            double newPrice = (Double) arg;
            System.out.println(name + " 收到通知: " + market.getStockSymbol() + 
                             " 价格变为: " + newPrice);
            
            // 根据价格变化做出交易决策
            if (newPrice > 100) {
                System.out.println(name + " 决定卖出");
            } else if (newPrice < 50) {
                System.out.println(name + " 决定买入");
            }
        }
    }
}

// 使用示例
public class JavaBuiltInObserverDemo {
    public static void main(String[] args) {
        StockMarket appleStock = new StockMarket("AAPL", 75.0);
        
        StockTrader trader1 = new StockTrader("王五");
        StockTrader trader2 = new StockTrader("赵六");
        
        appleStock.addObserver(trader1);
        appleStock.addObserver(trader2);
        
        // 模拟价格变化
        appleStock.setPrice(80.0);
        appleStock.setPrice(45.0);
        appleStock.setPrice(120.0);
    }
}

实际应用场景

1. GUI事件处理

在图形用户界面中,观察者模式被广泛应用:

// 简化版GUI事件处理
class Button {
    private List<ActionListener> listeners = new ArrayList<>();
    
    public void addActionListener(ActionListener listener) {
        listeners.add(listener);
    }
    
    public void click() {
        System.out.println("按钮被点击");
        ActionEvent event = new ActionEvent(this, System.currentTimeMillis(), "click");
        for (ActionListener listener : listeners) {
            listener.actionPerformed(event);
        }
    }
}

interface ActionListener {
    void actionPerformed(ActionEvent event);
}

class LoginAction implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent event) {
        System.out.println("执行登录操作");
    }
}

class LogAction implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent event) {
        System.out.println("记录操作日志");
    }
}

2. 消息队列系统

在分布式系统中,观察者模式常用于消息队列的实现:

// 简化的消息队列实现
class MessageQueue {
    private Map<String, List<MessageConsumer>> topicConsumers = new HashMap<>();
    
    public void subscribe(String topic, MessageConsumer consumer) {
        topicConsumers.computeIfAbsent(topic, k -> new ArrayList<>()).add(consumer);
    }
    
    public void publish(String topic, String message) {
        List<MessageConsumer> consumers = topicConsumers.get(topic);
        if (consumers != null) {
            for (MessageConsumer consumer : consumers) {
                consumer.consume(topic, message);
            }
        }
    }
}

interface MessageConsumer {
    void consume(String topic, String message);
}

class OrderService implements MessageConsumer {
    @Override
    public void consume(String topic, String message) {
        if ("order.created".equals(topic)) {
            System.out.println("订单服务处理新订单: " + message);
        }
    }
}

class InventoryService implements MessageConsumer {
    @Override
    public void consume(String topic, String message) {
        if ("order.created".equals(topic)) {
            System.out.println("库存服务更新库存: " + message);
        }
    }
}

观察者模式的优缺点

优点

  1. 松耦合:主题和观察者之间抽象耦合,彼此不需要知道具体实现
  2. 扩展性:可以方便地增加新的观察者,符合开闭原则
  3. 广播通信:支持一对多的通信方式
  4. 符合依赖倒置原则:依赖于抽象而不是具体实现

缺点

  1. 内存泄漏风险:如果观察者没有正确注销,可能导致内存泄漏
  2. 通知顺序不确定:多个观察者的通知顺序可能影响系统行为
  3. 性能问题:如果观察者数量很多,通知过程可能比较耗时

最佳实践和注意事项

1. 避免循环依赖

// 错误的实现 - 可能导致循环依赖
class ProblematicObserver implements Observer {
    private Subject subject;
    
    public ProblematicObserver(Subject subject) {
        this.subject = subject;
    }
    
    @Override
    public void update(String data) {
        // 在更新方法中又调用了主题的方法,可能导致循环调用
        if (data.contains("special")) {
            subject.someMethod(); // 危险的操作!
        }
    }
}

2. 使用弱引用避免内存泄漏

// 使用弱引用避免内存泄漏
class SafeSubject implements Subject {
    private List<WeakReference<Observer>> observers = new ArrayList<>();
    
    @Override
    public void registerObserver(Observer observer) {
        observers.add(new WeakReference<>(observer));
    }
    
    @Override
    public void notifyObservers() {
        Iterator<WeakReference<Observer>> iterator = observers.iterator();
        while (iterator.hasNext()) {
            WeakReference<Observer> ref = iterator.next();
            Observer observer = ref.get();
            if (observer != null) {
                observer.update("some data");
            } else {
                iterator.remove(); // 清理已被GC回收的观察者
            }
        }
    }
}

3. 异步通知提高性能

// 异步通知实现
class AsyncSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private ExecutorService executor = Executors.newCachedThreadPool();
    
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            executor.submit(() -> {
                try {
                    observer.update("async data");
                } catch (Exception e) {
                    // 处理异常,避免影响其他观察者
                    System.err.println("观察者处理异常: " + e.getMessage());
                }
            });
        }
    }
}

总结

观察者模式是构建松耦合、可扩展系统的强大工具。通过订阅-发布机制,它有效地解耦了对象之间的依赖关系,使得系统更加灵活和易于维护。在实际开发中,我们可以根据具体需求选择推模型或拉模型,也可以结合现代编程语言特性(如Java的Observable类)来简化实现。

无论你是构建GUI应用程序、消息系统,还是需要实现事件驱动的架构,观察者模式都能提供优雅的解决方案。只要注意避免常见陷阱(如内存泄漏、循环依赖等),这种模式将成为你工具箱中不可或缺的利器。

掌握观察者模式,不仅能够提升代码质量,更能帮助你以更加抽象和模块化的方式思考系统设计,这是成为优秀软件工程师的重要一步。

文档信息

Search

    Table of Contents