Java设计模式-创建型-观察者模式

目录

前言

示例

1.原生Java

2.jdk-Observable&Observer

2.spring-Observable&Observer

总结


前言

观察者模式对于我们来说,真是再简单不过了。无外乎两个操作,观察者订阅自己关心的主题和主题有数据变化后通知观察者们。

示例

1.原生Java

首先,需要定义主题,每个主题需要持有观察者列表的引用,用于在数据变更的时候通知各个观察者:

public class Subject {
    private List<Observer> observers = new ArrayList<Observer>();
    private int state;
    public int getState() {
        return state;
    }
    public void setState(int state) {
        this.state = state;
        // 数据已变更,通知观察者们
        notifyAllObservers();
    }
    // 注册观察者
    public void attach(Observer observer) {
        observers.add(observer);
    }
    // 通知观察者们
    public void notifyAllObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

定义观察者接口:

public abstract class Observer {
    protected Subject subject;
    public abstract void update();
}

其实如果只有一个观察者类的话,接口都不用定义了,不过,通常场景下,既然用到了观察者模式,我们就是希望一个事件出来了,会有多个不同的类需要处理相应的信息。比如,订单修改成功事件,我们希望发短信的类得到通知、发邮件的类得到通知、处理物流信息的类得到通知等。

我们来定义具体的几个观察者类:

public class BinaryObserver extends Observer {
    // 在构造方法中进行订阅主题
    public BinaryObserver(Subject subject) {
        this.subject = subject;
        // 通常在构造方法中将 this 发布出去的操作一定要小心
        this.subject.attach(this);
    }
    // 该方法由主题类在数据变更的时候进行调用
    @Override
    public void update() {
        String result = Integer.toBinaryString(subject.getState());
        System.out.println("订阅的数据发生变化,新的数据处理为二进制值为:" + result);
    }
}

public class HexaObserver extends Observer {
    public HexaObserver(Subject subject) {
        this.subject = subject;
        this.subject.attach(this);
    }
    @Override
    public void update() {
        String result = Integer.toHexString(subject.getState()).toUpperCase();
        System.out.println("订阅的数据发生变化,新的数据处理为十六进制值为:" + result);
    }
}

客户端使用也非常简单:

public static void main(String[] args) {
    // 先定义一个主题
    Subject subject1 = new Subject();
    // 定义观察者
    new BinaryObserver(subject1);
    new HexaObserver(subject1);

    // 模拟数据变更,这个时候,观察者们的 update 方法将会被调用
    subject.setState(11);
}

output:

订阅的数据发生变化,新的数据处理为二进制值为:1011
订阅的数据发生变化,新的数据处理为十六进制值为:B

当然,jdk 也提供了相似的支持,具体的大家可以参考 java.util.Observable 和 java.util.Observer 这两个类。

实际生产过程中,观察者模式往往用消息中间件来实现,如果要实现单机观察者模式,笔者建议读者使用 Guava 中的 EventBus,它有同步实现也有异步实现,本文主要介绍设计模式,就不展开说了。

还有,即使是上面的这个代码,也会有很多变种,大家只要记住核心的部分,那就是一定有一个地方存放了所有的观察者,然后在事件发生的时候,遍历观察者,调用它们的回调函数。

2.jdk-Observable&Observer

现在很多的购房者都在关注着房子的价格变化,每当房子价格变化的时候,所有的购房者都可以观察得到。

实际上以上的购房者就是观察者,他们所关注的房价就是被观察者。

其中要求,被观察者需要继承Observable类,观察则需要实现Observer接口

具体实现如下

房价的实现

 1 class House extends Observable{
 2     private double price;
 3     public House(double price){
 4         this.price=price;
 5     }
 6     public double getPrice(){
 7         return price;
 8     }
 9     public void setPrice(double price){
10         if(this.price!=price){
11             this.price=price;
12             setChanged();  //标注价格已经被更改
13             this.notifyObservers(price);  //通知观察者数据已被更改
14         }
15     }
16     @Override
17     public String toString() {
18         return "当前房价为:"+price;
19     }
20 }

购房者实现

 1 class HousePriceObserver implements Observer{
 2     private String name;
 3     public  HousePriceObserver(String name) {
 4         this.name=name;
 5     }
 6     @Override
 7     public void update(Observable o, Object arg) {
 8         //这里最好判断一下通知是否来自于房价,有可能来自其它地方
 9         if(o instanceof House){
10             System.out.println("购物者"+name+ "观察到房价已调整为:"+arg);
11         }
12     }
13 }

运行

   // 定义主题
 1 House house=new House(10000); 
   // 定义三个观察者
 2 HousePriceObserver A=new HousePriceObserver("A");
 3 HousePriceObserver B=new HousePriceObserver("B");
 4 HousePriceObserver C=new HousePriceObserver("C");
   // 主题 与 主题的观察者们
 5 house.addObserver(A);
 6 house.addObserver(B);
 7 house.addObserver(C);
 8 System.out.println(house);
   // 发布事件通知
 9 house.setPrice(6000);
10 house.setPrice(8000);

运行结果为:

当前房价为:10000.0
购物者C观察到房价已调整为:6000.0
购物者B观察到房价已调整为:6000.0
购物者A观察到房价已调整为:6000.0
购物者C观察到房价已调整为:8000.0
购物者B观察到房价已调整为:8000.0
购物者A观察到房价已调整为:8000.0

2.spring-Observable&Observer

‘主题’ & ‘主题的观察者们’:

package com.atta.msgdactuator.notification;

import com.atta.infra.msgdactuator.api.event.StatisticsEventMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Observable;

/**
 * 邮件生命周期事件发布者
 */
@Component
@Slf4j
public class EmailLifeCycleEventPublisher extends Observable implements InitializingBean {

    @Autowired
    private EmailLifeCycleEventListener emailLifeCycleEventObserver;

    @Override
    public void afterPropertiesSet() throws Exception {
        this.addObserver(emailLifeCycleEventObserver);
    }

    /**
     * 功能描述:事件通知。REPLIED,UNSUBSCRIBE,HARDBACK,SOFTBACK,NOT_SEND,SEND共计6个事件。
     *
     * 声明:
     *  1、异常由【调用事件发布者处】进行捕获
     *
     * @param statisticsEventMessage req
     */
    public void notify(StatisticsEventMessage statisticsEventMessage) {
        if (statisticsEventMessage == null){
            return;
        }
        super.setChanged();
        // 功能描述:1、内部是通过调用'订阅者'的update方法来进行通知的
        super.notifyObservers(statisticsEventMessage);
    }

}

观察者(此处指定义了一个观察者):

package com.atta.msgdactuator.notification;


import com.atta.infra.msgdactuator.api.event.StatisticsEventMessage;
import com.atta.msgdactuator.service.MessageTrackingStatisticsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

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

/**
 * 事件监听者
 * 职责:处理EmailLifeCycleEventPublisher 发布出来的StatisticsEventMessage事件
 */
@Component
@Slf4j
public class EmailLifeCycleEventListener implements Observer {

    @Value("${email.event.track.real.time.statistic.enabled:true}")
    private Boolean realTimeStatisticEnable;

    @Autowired
    private MessageTrackingStatisticsService messageTrackingStatisticsService;

    /**
     *  功能描述:订阅来自发布者的事件通知。REPLIED,UNSUBSCRIBE,HARDBACK,SOFTBACK,NOT_SEND,SEND共计6个事件。
     *
     *  声明:异常由【调用事件发布者处】进行捕获
     *
     * @param publisher
     * @param event
     */
    @Override
    public void update(Observable publisher, Object event) {
        if (publisher instanceof EmailLifeCycleEventPublisher && event instanceof StatisticsEventMessage) {
            // 参数说明:realTimeStatisticEnable是指是否开启事件实时统计开关(默认true-开启)
            if(realTimeStatisticEnable) {
                messageTrackingStatisticsService.notifyStatisticsEventMessage((StatisticsEventMessage) event);
            }
        }
    }
}

调用处:


@Service
@Slf4j
public class MessageTrackingStatisticsFacadeImpl implements MessageTrackingStatisticsFacade {

    @Autowired
    private MessageTrackingStatisticsService messageTrackingStatisticsService;

    @Autowired
    private EmailLifeCycleEventPublisher emailLifeCycleEventPublisher;

    @Override
    @AttaLogger(name = "MessageTrackingStatisticsFacadeImpl.notifyUnSubscribeStatisticsMsg", value = {"${param}", "${ret}"})
    public FacadeResponse<Void> notifyUnSubscribeStatisticsMsg(StatisticsEventMessage message) {
        FacadeResponse<Void> facadeResponse = new FacadeResponse<>();
        facadeResponse.setSucceeded(true);
        try{
            emailLifeCycleEventPublisher.notify(message);
        } catch (Exception e) {
            log.error("[MessageTrackingStatisticsFacadeImpl#notifyUnSubscribeStatisticsMsg] fail to notifyStatisticsMessage. taskId:[{}], subject:[{}], eventType:[{}], mid:[{}]",
                    message.getTaskId(), message.getSubject(), message.getType(), message.getMid(), e);
            facadeResponse.setSucceeded(false);
            facadeResponse.setResponseMsg(e.getMessage());
        }
        return facadeResponse;
    }
}

3.springboot-event

[发布-订阅]springboot事件管理器示例

总结

优点:
● 观察者和被观察者之间是抽象耦合 如此设计,则不管是增加观察者还是被观察者都非常容易扩展,而且在Java中都已经实现的抽象层级的定 义,在系统扩展方面更是得心应手。
● 建立一套触发机制 根据单一职责原则,每个类的职责是单一的,那么怎么把各个单一的职责串联成真实世界的复杂的逻辑关 系呢?比如,我们去打猎,打死了一只母鹿,母鹿有三个幼崽,因失去了母鹿而饿死,尸体又被两只秃鹰争 抢,因分配不均,秃鹰开始斗殴,然后羸弱的秃鹰死掉,生存下来的秃鹰,则因此扩大了地盘……这就是一个 触发机制,形成了一个触发链。观察者模式可以完美地实现这里的链条形式。

缺点:
观察者模式需要考虑一下开发效率和运行效率问题,一个被观察者,多个观察者,开发和调试就会比较复 杂,而且在Java中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的执行效率。在这种情况下,一 般考虑采用异步的方式。

使用场景:
● 事件多级触发场景。
● 跨系统的消息交换场景,如消息队列的处理机制

使用案例:spring 事件广播机制就是使用了观察者模式。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java设计模式是一组经过实践验证的面向对象设计原则和模式,可以帮助开发人员解决常见的软件设计问题。下面是常见的23种设计模式: 1. 创建模式(Creational Patterns): - 工厂方法模式(Factory Method Pattern) - 抽象工厂模式(Abstract Factory Pattern) - 单例模式(Singleton Pattern) -模式(Prototype Pattern) - 建造者模式(Builder Pattern) 2. 结构模式(Structural Patterns): - 适配器模式(Adapter Pattern) - 桥接模式(Bridge Pattern) - 组合模式(Composite Pattern) - 装饰器模式(Decorator Pattern) - 外观模式(Facade Pattern) - 享元模式(Flyweight Pattern) - 代理模式(Proxy Pattern) 3. 行为模式(Behavioral Patterns): - 责任链模式(Chain of Responsibility Pattern) - 命令模式(Command Pattern) - 解释器模式(Interpreter Pattern) - 迭代器模式(Iterator Pattern) - 中介者模式(Mediator Pattern) - 备忘录模式(Memento Pattern) - 观察者模式(Observer Pattern) - 状态模式(State Pattern) - 策略模式(Strategy Pattern) - 模板方法模式(Template Method Pattern) - 访问者模式(Visitor Pattern) 4. 并发模式(Concurrency Patterns): - 保护性暂停模式(Guarded Suspension Pattern) - 生产者-消费者模式(Producer-Consumer Pattern) - 读写锁模式(Read-Write Lock Pattern) - 信号量模式(Semaphore Pattern) - 线程池模式(Thread Pool Pattern) 这些设计模式可以根据问题的特点和需求来选择使用,它们提供了一些可复用的解决方案,有助于开发高质量、可维护且易于扩展的软件系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@来杯咖啡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值