观察者模式-对象间的联动

有个商城小程序,用户希望当有新品上市的时候能通知他们。这样用户就可以不要时刻盯着小程序了。在这个场景中,用户向小程序订阅了一个服务------发送新品短信。小程序在有新品上线时负责向订阅客户发出这个消息。

这就是发布-订阅模式,也称观察者模式。

1 观察者模式

是使用频率最高的设计模式之一。定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并自动更新。

图 观察者模式UML

Subject,目标类。是指被观察的对象,在目标中定义了一个观察者集合,一个观察目标可以接受任意数量的观察者来观察,它提供了一系列方法来增加和删除观察者对象,同时定义了通知方法notify(),目标类可以是接口,也可以是抽象或具体类。

ConcreteSubject,具体目标。是目标类的子类,通常包含有经常发生改变的数据。当它当状态发生改变时,向其各个观察者发出通知。同时它还实现了在目标类中定义的抽象业务逻辑方法(如果有)。如果无须扩展目标类,则具体目标类可以省略。

Observer,观察者。观察者将对观察目标的改变做出反应。观察者一般定义为接口。该接口声明了更新数据的方法update()。

ConcreteObserver,具体观察者。实现了Observer中声明的update()方法。通常在实现时,可以调用具体目标类的attach()方法将自己添加到目标类的集合或通过detach()方法将自己从目标类的集合中删除。

复制代码
public interface Observer {
    void update(String message);
}

public class MessageSubject {

    private final List<Observer> observerList = new ArrayList<>();

    public void attach(Observer observer) {
        this.observerList.add(observer);
    }

    public void detach(Observer observer) {
        this.observerList.remove(observer);
    }

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

}

public class AppletMessageSubject extends MessageSubject {

    @Override
    public void notifyObservers(String message) {
        super.notifyObservers(message);
        System.out.println("小程序平台日志记录,消息发送成功:" + message);
    }
}

public class ApiObserver implements Observer{

    @Override
    public void update(String message) {
        System.out.println("商品推送开始:" + message);
    }

}

public class UserObserver implements Observer{

    @Override
    public void update(String message) {
        System.out.println("好的。我知道了,我准备购买:" + message);
    }
}

public class ShopWeb {

    public static void main(String[] args) {
        MessageSubject subject = new AppletMessageSubject();

        Observer userObserver = new UserObserver();
        Observer apiObserver = new ApiObserver();

        subject.attach(userObserver);
        subject.attach(apiObserver);

        subject.notifyObservers("IPhone 15");
        subject.notifyObservers("Mate 16");
    }

}

//好的。我知道了,我准备购买:IPhone 15
//商品推送开始:IPhone 15
//小程序平台日志记录,消息发送成功:IPhone 15
//好的。我知道了,我准备购买:Mate 16
//商品推送开始:Mate 16
//小程序平台日志记录,消息发送成功:Mate 16

1.1 JDK 对观察者模式的支持

在JDK的java.util包中,提供了Observable类以及Observer接口,它们构成了JDK 对观察者模式的支持。

图 Observable的域与方法

图 Observer 接口

需求:求职者订阅了某boss招聘软件职位发布信息,当有新的职位发布时,会通知给求职者。求职者收到信息后,投递简历。

复制代码
public class BossObservable extends Observable {

    @Override
    public void notifyObservers(Object arg) {
        super.setChanged();
        super.notifyObservers(arg);
        System.out.println("记录日志,职位信息推送成功:" + arg);
    }
}

public class EmployeeObserver implements Observer {

    private String name;

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

    @Override
    public void update(Observable o, Object arg) {
        System.out.println(name + "。我钟意这个岗位:" + arg);
    }

}

public class Market {

    public static void main(String[] args) {
        Observable bossJob = new BossObservable();

        Observer employee1 = new EmployeeObserver("小李");
        Observer employee2 = new EmployeeObserver("小吴");

        bossJob.addObserver(employee1);
        bossJob.addObserver(employee2);

        bossJob.notifyObservers("Java 开发");
        bossJob.notifyObservers("全栈开发");
    }

}

//小吴。我钟意这个岗位:Java 开发
//小李。我钟意这个岗位:Java 开发
//记录日志,职位信息推送成功:Java 开发
//小吴。我钟意这个岗位:全栈开发
//小李。我钟意这个岗位:全栈开发
//记录日志,职位信息推送成功:全栈开发

2 优缺点

优点:

1)在观察目标和观察者之间建立一个抽象的耦合。观察目标只需维持一个抽象观察者集合,无须了解其具体观察者。

2)观察者模式支持广播通信,观察目标会向所有已注册的观察者对象发送通知。

缺点:

1)如果一个观察目标对象有许多观察者,将所有观察者都通知到会花费很多时间。

2)如果在观察者和观察目标之间存在循环依赖,观察目标会触发它们之间进行循环调用,可能导致系统奔溃。

3 适用场景

1)一个对象的改变将导致一个或多个其他对象也发生改变,而并不知道具体有多少对象及具体的对象。

2)需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象...可以使用观察者模式创建一种链式触发机制。

相关推荐
皮皮林5511 小时前
Java性能调优黑科技!1行代码实现毫秒级耗时追踪,效率飙升300%!
java
冰_河1 小时前
QPS从300到3100:我靠一行代码让接口性能暴涨10倍,系统性能原地起飞!!
java·后端·性能优化
桦说编程4 小时前
从 ForkJoinPool 的 Compensate 看并发框架的线程补偿思想
java·后端·源码阅读
躺平大鹅6 小时前
Java面向对象入门(类与对象,新手秒懂)
java
初次攀爬者7 小时前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺7 小时前
搞懂@Autowired 与@Resuorce
java·spring boot·后端
Derek_Smart8 小时前
从一次 OOM 事故说起:打造生产级的 JVM 健康检查组件
java·jvm·spring boot
NE_STOP9 小时前
MyBatis-mybatis入门与增删改查
java
孟陬12 小时前
国外技术周刊 #1:Paul Graham 重新分享最受欢迎的文章《创作者的品味》、本周被划线最多 YouTube《如何在 19 分钟内学会 AI》、为何我不
java·前端·后端