掘金 后端 ( ) • 2024-04-26 13:16

大家好,我是徒手敲代码。

今天来介绍一下观察者模式。

想象一个场景,小明关注了一个公众号,当公众号更新推文时,小明会收到通知。此时,小明可以选择是否查看这些更新的推文,而不用主动点开这个公众号的主页,才知道他有没有更新。

观察者模式总共有两个东西,一个是观察者,一个是被观察者。上述场景中,小明是观察者,而公众号是被观察者。而公众号有多个粉丝,相应的,被观察者需要维护一个观察者的列表,当被观察者的状态发生变化时,会自动通知这些观察者,使得观察者做出动作。

微信图片_20240425171509.png

这种模式实现了对象间的松耦合,允许观察者和被观察者之间的交互,而不需要彼此直接了解彼此的细节。

下面用代码来模拟出这个场景。

一个公众号的类,一个小明的类,

假设不用观察者模式,公众号发布文章,小明是无感知的,需要每隔一段时间就去看看是否更新

class OfficialAccount {
    private List<String> articles = new ArrayList<>();

    public void publishNewArticle(String article) {
        articles.add(article);
        System.out.println("公众号发布了新文章:" + article);
    }
}

class XiaoMing {
    public void checkForNewArticles(OfficialAccount officialAccount) {
        for (String article : officialAccount.getArticles()) {
            System.out.println("小明发现新文章:" + article);
        }
    }
}

// 使用示例
OfficialAccount officialAccount = new OfficialAccount();
XiaoMing xiaoMing = new XiaoMing();

officialAccount.publishNewArticle("Java零基础入门");
// 小明发现新文章:Java零基础入门
xiaoMing.checkForNewArticles(officialAccount); 

officialAccount.publishNewArticle("观察者模式详解");
// 小明发现新文章:观察者模式详解
xiaoMing.checkForNewArticles(officialAccount); 

在这个场景下,小明必须定期手动检查公众号是否有新推文,这不仅麻烦,还可能导致错过实时更新。

如果公众号一天发几十篇,小明怕是要每时每刻都盯着手机看!这种设计显然不够智能和高效,不符合现实生活中,我们期待的即时推送体验。

所以,我们需要引入观察者模式,让小明能够躺着接收新文章通知。

首先,定义一个观察者接口:

public interface Observer {
    void update(List<String> newArticles);
}

接着,改造公众号类,让它能维护一个观察者列表,并在发布新文章时通知所有观察者:

public class OfficialAccount {
    private List<String> articles = new ArrayList<>();
    private List<Observer> observers = new ArrayList<>();

    public void addObserver(Observer observer) {
        observers.add(observer);
    }

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

    public void publishNewArticle(String article) {
        articles.add(article);
        System.out.println("公众号发布了新文章:" + article);

        List<String> newArticles = Collections.singletonList(article);
        for (Observer observer : observers) {
            observer.update(newArticles);
        }
    }
}

最后,让小明成为观察者,实现Observer接口:

public class XiaoMing implements Observer {
    @Override
    public void update(List<String> newArticles) {
        for (String article : newArticles) {
            System.out.println("小明收到新文章通知:" + article);
        }
    }
}

// 使用示例
OfficialAccount officialAccount = new OfficialAccount();
XiaoMing xiaoMing = new XiaoMing();

officialAccount.addObserver(xiaoMing);

// 小明收到新文章通知:Java零基础入门
officialAccount.publishNewArticle("Java零基础入门"); 
// 小明收到新文章通知:观察者模式详解
officialAccount.publishNewArticle("观察者模式详解"); 

UML图如下:

微信图片_20240425174553.png

这样一来,公众号每次更新,都会调用其观察者的update方法,而不需要观察者主动去调。

如果想要继续增加新的观察者,只需要实现Observer即可,原有的类不受影响,解耦观察者和被观察者。

实际应用

在 Spring 框架中,ApplicationListener接口就运用了观察者模式。只要某个类实现了ApplicationListener接口,就会在该事件发生时接收到通知。

比如:ContextRefreshedEvent就是Spring提供的一种预定义事件,表示ApplicationContext(应用上下文)被初始化或刷新完成。