十四、行为型(观察者模式)

观察者模式(Observer Pattern)

概念

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象间的一对多依赖关系,当被观察的对象(主题)状态发生改变时,所有依赖它的对象(观察者)都会收到通知并自动更新。观察者模式广泛用于事件处理系统中。


应用场景

  1. 事件驱动的系统:在GUI应用程序中,当用户触发某些事件(如按钮点击、窗口关闭等),界面上多个组件可能需要做出响应。观察者模式在此时尤为合适,可以将事件源(Subject)和处理程序(Observer)解耦。

  2. 数据变化的广播机制:当数据发生变化时,多个依赖此数据的组件或模块需要更新。例如,在股市行情系统中,当股票价格更新时,所有订阅该股票的观察者都会收到通知。

  3. 发布/订阅系统:当一个对象(发布者)需要通知多个对象(订阅者)时,观察者模式可以动态注册和通知这些订阅者,从而实现发布/订阅的机制。


注意点

  • 性能问题:如果观察者较多或者通知非常频繁,可能会影响性能,因此需要注意观察者模式的使用场景,避免不必要的通知开销。
  • 内存泄漏:要避免忘记移除不再需要的观察者,尤其是在对象生命周期较短的情况下,可能会导致内存泄漏问题。
  • 通知顺序:多个观察者同时监听同一主题时,可能需要考虑通知的顺序问题。

核心要素

  1. Subject(主题/被观察者):维护观察者列表,负责添加、移除和通知观察者。
  2. Observer(观察者):定义一个更新接口,用于接收主题状态的变更通知。
  3. ConcreteSubject(具体主题):具体的主题对象,状态发生变化时通知所有的观察者。
  4. ConcreteObserver(具体观察者):具体的观察者对象,实现更新接口并根据通知做出相应动作。

Java代码完整示例

代码示例:简单观察者模式

java 复制代码
// 观察者接口
interface Observer {
    void update(String message);
}

// 具体观察者A
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);
    }
}

// 具体观察者B
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);
    }
}

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

// 具体主题
class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();

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

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

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

// 客户端代码
public class ObserverPatternDemo {
    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();

        Observer observer1 = new ConcreteObserverA("观察者1");
        Observer observer2 = new ConcreteObserverB("观察者2");

        subject.registerObserver(observer1);
        subject.registerObserver(observer2);

        // 通知所有观察者
        subject.notifyObservers("主题状态发生变化");

        // 移除观察者1,之后再次通知
        subject.removeObserver(observer1);
        subject.notifyObservers("第二次状态变化");
    }
}

输出结果

观察者1 收到通知: 主题状态发生变化
观察者2 收到通知: 主题状态发生变化
观察者2 收到通知: 第二次状态变化

各种变形用法完整示例

  1. 推模式 vs 拉模式

    • 推模式(Push):主题对象主动将变更的数据推送给所有观察者,观察者不需要主动请求数据。
    • 拉模式(Pull):观察者主动从主题对象拉取所需的数据,主题只通知有更新,但不提供具体数据。

    推模式示例(之前的代码已为推模式)

    java 复制代码
    // notifyObservers()方法中直接将消息推送给观察者
    subject.notifyObservers("推送的数据");

    拉模式示例

    java 复制代码
    // 观察者接口
    interface Observer {
        void update(Subject subject);
    }
    
    // 具体观察者
    class ConcreteObserver implements Observer {
        private String name;
    
        public ConcreteObserver(String name) {
            this.name = name;
        }
    
        @Override
        public void update(Subject subject) {
            // 主动拉取数据
            if (subject instanceof ConcreteSubject) {
                ConcreteSubject concreteSubject = (ConcreteSubject) subject;
                System.out.println(name + " 拉取到数据: " + concreteSubject.getState());
            }
        }
    }
    
    // 具体主题
    class ConcreteSubject implements Subject {
        private List<Observer> observers = new ArrayList<>();
        private String state;
    
        public String getState() {
            return state;
        }
    
        public void setState(String state) {
            this.state = state;
            notifyObservers();
        }
    
        @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(this);  // 仅通知更新,不推送数据
            }
        }
    }
    
    // 客户端代码
    public class ObserverPatternPullDemo {
        public static void main(String[] args) {
            ConcreteSubject subject = new ConcreteSubject();
            Observer observer = new ConcreteObserver("观察者");
    
            subject.registerObserver(observer);
    
            subject.setState("新的状态");
        }
    }

    输出结果

    观察者 拉取到数据: 新的状态
    
  2. Java内置的观察者模式实现

    Java标准库提供了java.util.Observerjava.util.Observable类,尽管它们现在被认为是过时的,但仍可以用来实现观察者模式。

    代码示例

    java 复制代码
    import java.util.Observable;
    import java.util.Observer;
    
    // 具体主题类,继承Observable
    class ConcreteSubject extends Observable {
        private String state;
    
        public String getState() {
            return state;
        }
    
        public void setState(String state) {
            this.state = state;
            setChanged();  // 标记状态已经改变
            notifyObservers(state);  // 推送通知给所有观察者
        }
    }
    
    // 具体观察者类,继承Observer接口
    class ConcreteObserver implements Observer {
        private String name;
    
        public ConcreteObserver(String name) {
            this.name = name;
        }
    
        @Override
        public void update(Observable o, Object arg) {
            System.out.println(name + " 收到更新: " + arg);
        }
    }
    
    public class ObserverPatternBuiltInDemo {
        public static void main(String[] args) {
            ConcreteSubject subject = new ConcreteSubject();
    
            ConcreteObserver observer1 = new ConcreteObserver("观察者1");
            ConcreteObserver observer2 = new ConcreteObserver("观察者2");
    
            subject.addObserver(observer1);
            subject.addObserver(observer2);
    
            subject.setState("主题状态已更改");
        }
    }

    输出结果

    观察者1 收到更新: 主题状态已更改
    观察者2 收到更新: 主题状态已更改
    
  3. 异步观察者模式

    如果通知的处理较为耗时,可以将通知的操作异步化,防止阻塞主题的状态变化。

    代码示例(使用线程实现异步通知)

    java 复制代码
    class AsyncObserver implements Observer {
        private String name;
    
        public AsyncObserver(String name) {
            this.name = name;
        }
    
        @Override
        public void update(String message) {
            // 启动新线程进行异步处理
            new Thread(() -> {
                try {
                    Thread.sleep(1000);  // 模拟耗时操作
                    System.out.println(name + " 异步处理收到的通知: " + message);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
    
    public class AsyncObserverPatternDemo {
        public static void main(String[] args) {
            ConcreteSubject subject = new ConcreteSubject();
    
            Observer asyncObserver1 = new AsyncObserver("异步观察者1");
            Observer asyncObserver2 = new AsyncObserver("异步观察者2");
    
            subject.registerObserver(asyncObserver1);
            subject.registerObserver(asyncObserver2);
    
            subject.notifyObservers("异步通知消息");
        }
    }

    输出结果

    异步观察者1 异步处理收到的通知: 异步通知消息
    异步观察者2 异步处理收到的通知: 异步通知消息
    

通过观察者模式,系统可以轻松扩展观察者,且实现了低耦合、高可扩展性。不同的变形(推拉模式、异步处理等)还可以帮助优化性能和增强可扩展性。

相关推荐
Q_1928499906几秒前
基于Spring Boot的摄影器材租赁回收系统
java·spring boot·后端
qq_433554541 分钟前
C++ 面向对象编程:+号运算符重载,左移运算符重载
开发语言·c++
Code_流苏3 分钟前
VSCode搭建Java开发环境 2024保姆级安装教程(Java环境搭建+VSCode安装+运行测试+背景图设置)
java·ide·vscode·搭建·java开发环境
数据小爬虫@20 分钟前
如何高效利用Python爬虫按关键字搜索苏宁商品
开发语言·爬虫·python
ZJ_.22 分钟前
WPSJS:让 WPS 办公与 JavaScript 完美联动
开发语言·前端·javascript·vscode·ecmascript·wps
Narutolxy28 分钟前
深入探讨 Go 中的高级表单验证与翻译:Gin 与 Validator 的实践之道20241223
开发语言·golang·gin
Hello.Reader35 分钟前
全面解析 Golang Gin 框架
开发语言·golang·gin
禁默1 小时前
深入浅出:AWT的基本组件及其应用
java·开发语言·界面编程
Cachel wood1 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Code哈哈笑1 小时前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习