首页 > 其他分享 >观察者模式的简单使用

观察者模式的简单使用

时间:2023-08-26 20:33:33浏览次数:32  
标签:缓存 void 观察者 模式 update 事件 简单 public

定义

观察者模式定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。

观察者模式中有两个角色,一个是主题(Subject),一个是观察者(Observer)。观察者会观察主题,当主题发生了变化,观察者会做出相应的处理。主题跟观察者的关系是一对多。观察者模式也叫发布订阅模式或者事件监听模式。比如很多主播会说,”点关注,不迷路“。当主播上线时,那些订阅了主播的用户会收到通知消息。

简单观察者实现

主题提供三个行为:

  1. 注册功能:这样观察者才能订阅道它。
  2. 取消注册功能:观察者能随时取消订阅。
  3. 通知功能:告诉观察者们有变化。

因此主题会依赖观察者,在它内部维护一个观察者集合,负责观察者的初始化和删除。

观察者要提供一个行为:

  1. 更新功能:观察者收到通知消息后,随即做出自己的逻辑处理。

观察者模式结构图

需求描述

举个例子。有个商城项目,我们要设计一个缓存模型,要把项目中的重要的实体对象,比如有商品对象goods、订单对象order等,在服务启动时从 MySQL 库中查出来,存入 Redis 缓存,然后供服务层使用。项目中有一个数据管理服务,它专门负责实体数据的增删改,比如上架、修改、删除一些商品。数据管理服务的操作修改的是 MySQL 库中的数据。当数据管理服务更新了库里的数据,就要立即通知缓存层更新 Redis 缓存。缓存层肯定有不同的缓存类,比如 GoodsCacheOrderCache等。

代码实现

我们可以简单的把数据管理服务当作是一个主题,它变化了(增、删、改),要通知缓存类更新缓存数据。那么缓存类就看作是观察者。

首先是数据管理服务这个主题接口:

public interface DataManageSubject {
    /**
     * 注册观察者
     * @param observer 观察者
     */
    void register(ICacheObserver observer);

    /**
     * 删除观察者
     * @param observer 观察者
     */
    void unregister(ICacheObserver observer);

    /**
     * 通知观察者
     */
    void notifyObservers();
}

缓存类对应观察者接口:

public interface ICacheObserver {
    void update();
}

主题实现类:

public class DataManageSubjectImpl implements DataManageSubject {

    private List<ICacheObserver> observers = new ArrayList<>();

    @Override
    public void register(ICacheObserver observer) {
        observers.add(observer);
    }

    @Override
    public void unregister(ICacheObserver observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        observers.forEach(ICacheObserver::update);
    }
    
    public void updateData() {
        System.out.println("admin update mysql data...");
    }
}

观察者实现类:

public class GoodsCache implements ICacheObserver {
    @Override
    public void update() {
        System.out.println("query data from db and set into redis successfully");
    }
}

public class OrderCache implements ICacheObserver {
    @Override
    public void update() {
        System.out.println("query data from db and set into redis successfully");
    }
}

当数据管理服务有了数据更新操作后,它需要主动通知缓存类。

public class DataManageEventPublisher {
    public static void main(String[] args) {
        DataManageSubject dataManage = new DataManageSubjectImpl();

        // 注册观察者
        GoodsCache goodsCache = new GoodsCache();
        OrderCache orderCache = new OrderCache();
        dataManage.register(goodsCache);
        dataManage.register(orderCache);

        // 更新数据
        dataManage.updateData();

		// 通知观察者
        dataManage.notifyObservers();
    }
}

以上代码只为简单描述观察者模式的数据结构。在 JDK 中有内置的观察者模式,java.util包下有Observer接口,和一个Observable类,Observable是一个抽象类,他就是主题,Observer就是观察者。从这个代码中我们可以体会到观察者模式的设计思路。

但是它的缺点就是主题跟观察者强耦合了,其实可以把它们的耦合关系交给DataManageEventPublisher管理,这样主题跟观察者就能解耦了。

在 Spring 中应用观察者模式

观察者思想的应用之一就是事件监听机制。事件监听机制应用广泛,事件(Event)就是一个主题,多个监听器(Listener)监听这个主题。JDK 也设计了事件监听的模型,java.util.EventObjectjava.uitl.EventListener就是监听机制的顶级父类,Spring 中事件监听模型就是继承这两个类。

再来看事件监听机制中的角色:

  1. 事件及事件源:事件拥有事件源,它是通过事件对象的构造器传入的一个Object对象。事件源是逻辑上被认定为该事件最初发生的对象。说白了,事件源就是用来描述和初始化一个事件。最重要的是,是EventObject定义了这么一个成员变量。
  2. 监听器:对应观察者模式中的观察者。监听特定事件,并在内部定义了事件发生后的响应逻辑。
  3. 事件发布器:事件发布器负责对外提供事件发布和增删监听器的接口,维护事件和监听器的映射关系,并在事件发生时通知监听器。也就是说,事件发布器维护了事件和监听器的关系,将它们解耦了。

接下来看代码实现:

1、服务启动时,加载数据到 Redis

/**
 * 启动监听器
 */
@Component
public class StartupListener implements ApplicationListener<ContextRefreshedEvent> {

    @Autowired(required=false)
    private StartupCache[] startupCaches;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (event.getApplicationContext().getParent() == null) {
            Objects.requireNonNull(startupCaches, "testBeans not initialized...");

            for (StartupCache startupCache : startupCaches) {
                startupCache.onStartup();
            }
        }
    }
}

/**
 * 启动缓存基类
 */
public abstract class StartupCache {

    public abstract void onStartup();
}

2、数据变更事件和事件源

/**
 * 数据管理服务数据变更事件
 */
public class DataChangeEvent extends ApplicationEvent {

    public DataChangeEvent(DataChangeSource source) {
        super(source);
    }
}

/**
 * 数据变更事件源
 */
@Data
public class DataChangeSource {
    /**
     * 更新的是哪一种数据
     */
    private String type;

    /**
     * 操作类型
     */
    private String operation;
}

3、监听器:缓存层的类都是监听器,随时监听数据管理服务的事件

/**
 * 商品缓存,相当于监听器角色
 */
@Repository
public class GoodsCache extends StartupCache {

    private static final String DATA_TYPE = "goods";

    @Override
    public void onStartup() {
        System.out.println("server start: query all from db and set into redis");
    }

    @EventListener(DataChangeEvent.class)
    public void refresh(DataChangeEvent event) {
        DataChangeSource source = (DataChangeSource)event.getSource();
        if (DATA_TYPE.equals(source.getType())) {
            if ("update".equals(source.getOperation())) {
                update();
            }
        }
    }

    public void update() {
        System.out.println("goodsCache update redis cache successfully...");
    }

}

注意:使用@EventListener跟实现ApplicationListener是一样的效果,所以选择更简洁的注解方式。

4、商品服务类,模拟数据更新操作

/**
 * 商品Service,模拟数据管理服务中对商品的数据更新操作
 */
@Service
public class GoodsService {

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    public void update() {
        // 模拟商品数据更新
        System.out.println("data manager update goods data in db...");
        
        // 发布事件
        DataChangeSource dataChangeSource = new DataChangeSource();
        dataChangeSource.setOperation("update");
        dataChangeSource.setType("goods");
        applicationEventPublisher.publishEvent(new DataChangeEvent(dataChangeSource));
    }
}

5、测试类,当数据管理服务更新商品信息,缓存类同步更新缓存。

public class CacheModelTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

        // GoodsService更新数据
        GoodsService goodsService = context.getBean(GoodsService.class);
        goodsService.update();
    }
}
server start: query all from db and set into redis
data manager update goods data in db...
goodsCache update redis cache successfully...

总结

这种写法也同样适用于 Spring Boot。

参考资料

深入理解Spring的容器内事件发布监听机制
Spring Events

标签:缓存,void,观察者,模式,update,事件,简单,public
From: https://www.cnblogs.com/cloudrich/p/17659395.html

相关文章

  • 剑指 Offer 68 - II. 二叉树的最近公共祖先(简单)
    题目:classSolution{public:TreeNode*lowestCommonAncestor(TreeNode*root,TreeNode*p,TreeNode*q){if(root==p||root==q||root==nullptr)returnroot;//如果当前节点为空或者当前节点即为其中某个指定节点TreeNode*left=lowestCommo......
  • 剑指 Offer 55 - II. 平衡二叉树(简单)
    题目:classSolution{public:intgetHeight(TreeNode*cur){//递归函数返回的是以当前节点为根节点的高度。if(!cur)return0;//空节点的高度为0intleftHeight=getHeight(cur->left);//取得左节点的高度if(leftHeight=......
  • 【23种设计模式】设计模式综述(开篇)
    一、设计模式概述:​设计模式(Designpattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。是一套被反复使用......
  • 【23种设计模式】单例模式(一)
    前言:单例模式是创建型模式5种中的第1种,关注对象的创建,保证一个类仅有一个实例,并且提供一个全局访问点。在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。如何绕过常规的构造器,提供一种机制来保证一个类只......
  • 单例模式
    3.4.1单例模式中的角色和职责单例模式的标准类图如下:Singleton(单例):在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个Singleton类型的静态对象,作为外......
  • 阿里Nacos配置中心简单使用
    简介Nacos是DynamicNamingandConfigurationService的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。Nacos致力于帮助你发现、配置和管理微服务。Nacos提供了一组简单易用的特性集,帮助你快速实现动态服务发现、服务配置、服务元数据及流......
  • 一个简单的spdlog使用示例
    目录引用源码封装Log头文件使用方法spdlog是一个开源、跨平台、无依赖、只有头文件的C++11日志库,网上介绍的文章有很多这里就不过多的介绍了,GitHub链接:https://github.com/gabime/spdlog。引用源码先下载spdlog的源码,将源码的include文件夹复制到自己的项目文件夹下:然后在项......
  • 设计模式-命令模式
    命令模式模式定义命令模式是一种行为定义模式,可以将请求转换成一个与请求相关的,包含该请求所有信息的独立对象,并且能够根据不同请求将方法参数化,延迟请求执行或者将其放入到队列中且能实现撤销等操作模式动机敏捷开发的原则要求,不要在代码上添加基于猜测的,实际上不需要的功......
  • 磁盘配置的3种模式
    磁盘置备的3种模式,工作中在VMwareEsxi上虚拟服务器配置虚拟硬件、威联通NAS磁盘配置上遇到过。下面介绍一下这3种模式 所谓磁盘置备,就是磁盘空间分配的技术。精简置备厚置备(延迟置零):假如说新分配的硬盘上有数据存在,配置完后硬盘上有新数据产生时,新数据会逐渐覆盖硬盘上的旧......
  • Arthas简单入门
    简介Arthas是一款线上监控诊断产品,通过全局视角实时查看应用load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。当你遇到以下类似问题而束手无策时,Arthas......