【设计模式-4.1】行为型——观察者模式

说明:本文介绍设计模式中行为型设计模式中的,观察者模式;

商家与顾客

观察者模式属于行为型设计模式,关注对象的行为。以商家与顾客为例,商家有商品,顾客来购买商品,如果商家商品卖完了,顾客则购买失败。如下:

(Shop,商家类,提供购买、进货方法)

/**
 * 商家
 */
public class Shop {

    /**
     * 商品
     */
    private String product;

    public Shop() {
        this.product = "无商品";
    }

    /**
     * 出货
     * @return
     */
    public String getProduct() {
        return product;
    }

    /**
     * 进货
     * @param product
     */
    public void setProduct(String product) {
        this.product = product;
    }
}

(Buyer,顾客类,注入商店对象,购买商店商品)

/**
 * 顾客
 */
public class Buyer {

    private String name;

    private Shop shop;

    public Buyer(String name, Shop shop) {
        this.name = name;
        this.shop = shop;
    }

    /**
     * 买家购买商品
     */
    public void buy() {
        System.out.println(name + "来购买:" + shop.getProduct());
    }
}

(Client,客户端,演示购买动作)

/**
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        Shop shop = new Shop();
        new Buyer("张三", shop).buy();

        shop.setProduct("橘子");
        new Buyer("李四", shop).buy();
    }
}

(执行结果)

在这里插入图片描述

分析上面的行为,可以发现一点缺陷。当商家的商品发生变化时,无法主动的,自发的通知顾客,只有顾客来购买时,才知道商品的信息。因此,我们考虑是否可以在商家类内部维护一个“顾客列表”,当商品信息发生变化时,调用顾客列表中所有顾客对应的方法,达到主动通知客户的目的。如下:

(Shop,商家类,维护一个顾客列表,新增一个通知顾客的方法)

import java.util.ArrayList;
import java.util.List;

/**
 * 商家
 */
public class Shop {

    /**
     * 商品
     */
    private String product;

    /**
     * 顾客列表
     */
    private List<Buyer> buyers;

    public Shop() {
        this.product = "无商品";
        this.buyers = new ArrayList<>();
    }

    /**
     * 添加顾客
     * @param buyer
     */
    public void addBuyer(Buyer buyer) {
        this.buyers.add(buyer);
    }
    
    /**
     * 移除顾客
     * @param buyer
     */
    public void removeBuyer(Buyer buyer) {
        this.buyers.remove(buyer);
    }

    /**
     * 出货
     * @return
     */
    public String getProduct() {
        return product;
    }

    /**
     * 进货
     * @param product
     */
    public void setProduct(String product) {
        this.product = product;
        notifyBuyers();
    }

    /**
     * 通知顾客
     */
    public void notifyBuyers() {
        for (Buyer buyer : buyers) {
            buyer.buy(product);
        }
    }
}

(Buyer,顾客抽象类,创建一个购买商品的抽象方法)

/**
 * 顾客
 */
public abstract class Buyer {

    /**
     * 顾客姓名
     */
    protected String name;

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

    /**
     * 买家购买商品
     */
    public abstract void buy(String product);
}

(FruitBuyer,水果买家,只购买水果类的商品)

/**
 * 水果买家
 */
public class FruitBuyer extends Buyer{

    public FruitBuyer(String name) {
        super(name);
    }

    @Override
    public void buy(String product) {
        if (product.contains("水果")) {
            System.out.println(name + ":来购买了水果");
        }
    }
}

(PhoneBuyer,手机买家,只购买手机类的商品)

/**
 * 手机买家
 */
public class PhoneBuyer extends Buyer{

    public PhoneBuyer(String name) {
        super(name);
    }

    @Override
    public void buy(String product) {
        if (product.contains("手机")) {
            System.out.println(name + ":来购买了手机");
        }
    }
}

(Client,客户端,模拟购买行为,张三只购买手机,买了一次后就不再需要了)

/**
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        Shop shop = new Shop();
        PhoneBuyer phoneBuyer = new PhoneBuyer("张三");
        FruitBuyer fruitBuyer = new FruitBuyer("李四");

        shop.addBuyer(phoneBuyer);
        shop.addBuyer(fruitBuyer);

        shop.setProduct("苹果手机");
        shop.setProduct("各种水果");
        shop.removeBuyer(phoneBuyer);

        System.out.println("=================水果和手机降价了=================");
        shop.setProduct("水果和手机降价了");

    }
}

执行结果。当商家的商品信息有变化时,会通知到对应的顾客来购买。

在这里插入图片描述

类比现实生活,小村庄里只有一家商店。我们需要购买某样日用品,去商店购买,店长说没有,下个月才进货。很自然的,我们会和店长说,那我留下电话,进货了打电话通知我。

其中,留下电话,可以类比为将顾客注册到商店类中,而打电话则是调用顾客类对应的方法。

Spring中的体现

观察者模式,是一种订阅-发布的模式。在上面的例子中,商家是发布者,顾客是订阅者,当商家商品信息有变动时,发布信息,顾客接收。

Spring框架中,就有观察者模式的应用。如下:

(Customer,消费者类)

import org.springframework.context.ApplicationEvent;

public class Customer extends ApplicationEvent {

    private String message;

    public Customer(Object source, String message) {
        super(source);
        this.message = message;
    }


    public String getMessage() {
        return message;
    }
}

(CustomListener1,消费者1号)

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class CustomListener1 implements ApplicationListener<Customer> {

    @Override
    public void onApplicationEvent(Customer customer) {
        System.out.println("1号订阅者收到消息: " + customer.getMessage());
    }
}

(CustomListener2,消费者2号)

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class CustomListener2 implements ApplicationListener<Customer> {

    @Override
    public void onApplicationEvent(Customer customer) {
        System.out.println("2号订阅者收到消息: " + customer.getMessage());
    }
}

(Publisher,发布者)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Component
public class Publisher {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    public void publishCustomEvent(String message) {
        eventPublisher.publishEvent(new Customer(this, message));
    }
}

(在测试类中,发布信息)

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest
@RunWith(SpringRunner.class)
public class PublisherTest {

    @Autowired
    private Publisher publisher;

    @Test
    public void test1() {
        publisher.publishCustomEvent("Hello World!");
    }
}

执行结果,2个订阅者都收到了消息;

在这里插入图片描述

以上就是观察者模式的实现;

总结

本文参考《设计模式的艺术》、《秒懂设计模式》两书

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/213136.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

go语言学习-并发编程(并发并行、线程协程、通道channel)

1、 概念 1.1 并发和并行 并发:具有处理多个任务的能力 (是一个处理器在处理任务)&#xff0c;cpu处理不同的任务会有时间错位&#xff0c;比如有A B 两个任务&#xff0c;某一时间段内在处理A任务&#xff0c;这时A任务需要停止运行一段时间&#xff0c;那么会切换到处理B任…

DockerFile常用保留字指令及知识点合集

目录 DockerFile加深理解&#xff1a; DockerFile常用保留字指令 保留字&#xff1a; RUN&#xff1a;容器构建时需要运行的命令 COPY&#xff1a;类似ADD&#xff0c;拷贝文件和目录到镜像中。 将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 …

【动态规划】LeetCode-面试题 17.16. 按摩师

&#x1f388;算法那些事专栏说明&#xff1a;这是一个记录刷题日常的专栏&#xff0c;每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新1道题目&#xff0c;在这立下Flag&#x1f6a9; &#x1f3e0;个人主页&#xff1a;Jammingpro &#x1f4d5;专栏链接&…

vs 安装 qt qt扩展

1 安装qt 社区版 免费 Download Qt OSS: Get Qt Online Installer 2 vs安装 qt vs tools 3 vs添加 qt添加 bin/cmake.exe 路径 3.1 扩展 -> qt versions 3.2

【STM32】STM32学习笔记-新建工程(04)

00. 目录 文章目录 00. 目录01. 创建STM32工程02. STM32工程编译和下载03. LED测试04. 型号分类及缩写05. 工程结构06. 附录 01. 创建STM32工程 【STM32】STM32F103C8T6 创建工程模版详解(固件库) 02. STM32工程编译和下载 2.1 选择下载器位ST-Link Debugger 2.2 勾选上电…

04. 函数

目录 1、前言 2、Python中的函数 2.1、内置函数 2.2、自定义函数 2.3、函数调用 3、函数的参数 3.1、形参和实参 3.2、位置参数&#xff08;Positional Arguments&#xff09; 3.3、默认参数&#xff08;Default Arguments&#xff09;&#xff1a; 3.4、关键字参数&a…

如何为C#WinFrom编译的.exe添加个性化图标

1、在VS中点击菜单栏上的“项目”,找到最下面的属性&#xff0c;单击进去 2、加载自定义的.ico文件&#xff0c;如果没有此格式的文件可以使用此网站去转换&#xff1a;图标制作大师 - 轻松制作网站favicon图标 3、重新编译文件即可

【【水 MicroBlaze 最后的介绍和使用】】

水 MicroBlaze 最后的介绍和使用 我对MicroBlaze 已经有了一个普遍的理解 了 现在我将看的两个 一个是 AXI4接口的 DDR读写实验 还有一个是 AXI DMA 环路实验 虽然是 水文 但是 也许能从中 得到一些收获 第一个是 AXI DDR 读写实验 Xilinx 从 Spartan-6 和 Virtex-6 系列开始…

SSM框架(六):SpringBoot技术及整合SSM

文章目录 一、概述1.1 简介1.2 起步依赖1.3 入门案例1.4 快速启动 二、基础配置2.1 三种配置文件方式2.2 yaml文件格式2.3 yaml读取数据方式&#xff08;3种&#xff09; 三、多环境开发3.1 yml文件-多环境开发3.2 properties文件-多环境开发3.3 多环境命令行启动参数设置3.4 多…

【数值计算方法(黄明游)】函数插值与曲线拟合(一):Lagrange插值【理论到程序】

​ 文章目录 一、近似表达方式1. 插值&#xff08;Interpolation&#xff09;2. 拟合&#xff08;Fitting&#xff09;3. 投影&#xff08;Projection&#xff09; 二、Lagrange插值1. 天书1. 人话拉格朗日插值方法a. 线性插值&#xff08;n1&#xff09;基本思想线性插值与线…

解决uview中uni-popup弹出层不能设置高度问题

开发场景&#xff1a;点击条件筛选按钮&#xff0c;在弹出的popup框中让用户选择条件进行筛选 但是在iphone12/13pro展示是正常&#xff0c;但是切换至其他手机型号就填充满了整个屏幕&#xff0c;需要给这个弹窗设置一个固定的高度 iphone12/13pro与其他型号手机对比 一开始…

智能优化算法应用:基于海洋捕食者算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于海洋捕食者算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于海洋捕食者算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.海洋捕食者算法4.实验参数设定5.算法结果…

工业机器视觉megauging(向光有光)使用说明书(四,轻量级的visionpro)

第三个相机的添加&#xff0c;突然发现需要补充一下&#xff1a; 第一步&#xff0c;假定你对c#编程懂一点&#xff0c;我们添加了一个页面“相机三”在tabcontrol1&#xff1a; 第二步&#xff0c;添加dll到工具箱&#xff1a; 第三步&#xff0c;点击‘浏览’&#xff0c;找…

Web前端JS如何获取 Video/Audio 视音频声道(左右声道|多声道)、视音频轨道、音频流数据

写在前面&#xff1a; 根据Web项目开发需求&#xff0c;需要在H5页面中&#xff0c;通过点击视频列表页中的任意视频进入视频详情页&#xff0c;然后根据视频的链接地址&#xff0c;主要是 .mp4 文件格式&#xff0c;在进行播放时实时的显示该视频的音频轨道情况&#xff0c;并…

Fiddler抓包工具之Fiddler+willow插件应用

安装Fiddler的安装包地址&#xff1a;fillderwillow 解压后安装fiddler4和willow1.4.*版本。 安装成功后&#xff0c;启动fiddler后会出现willow插件按钮&#xff1a; 说明安装成功。 重定向 willow重定向 进入willow界面后&#xff0c;通过右键->Add Project ->Add Ru…

canvas基础:fillStyle 和strokeStyle示例

canvas实例应用100 专栏提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。 canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重要的帮助。 文章目录 上色…

Spring Task 超详解版

目录 一、定时任务的理解 二、入门案例 三、Cron表达式 四、Cron实战案例 五、多线程案例 一、定时任务的理解 定时任务即系统在特定时间执行一段代码&#xff0c;它的场景应用非常广泛&#xff1a; 购买游戏的月卡会员后&#xff0c;系统每天给会员发放游戏资源。管理系…

基于姿态估计的3D动画生成

在本文中&#xff0c;我们将尝试通过跟踪 2D 视频中的动作来渲染人物的 3D 动画。 在 3D 图形中制作人物动画需要大量的运动跟踪器来跟踪人物的动作&#xff0c;并且还需要时间手动制作每个肢体的动画。 我们的目标是提供一种节省时间的方法来完成同样的任务。 我们对这个问题…

EasyMetagenome易宏基因组——简单易用的宏基因组分析流程-来自刘永鑫团队的秘密武器

原仓库地址如下&#xff0c;github有时候无法访问&#xff0c;等一段时间再试就行&#xff1a; YongxinLiu/EasyMetagenome: Easy Metagenome Pipeline (github.com) 相关文章&#xff0c;看文章更清晰这个可干啥&#xff1a; EasyAmplicon: An easy‐to‐use, open‐source…

JAVA高级-1

常用API 第一章 API 产品说明书 第二章 Scanner类&#xff08;输入&#xff09; 功能&#xff1a;获取键盘输入 package day7_12.demo01_Scanner;import java.util.Scanner; //1、导包 /* 功能&#xff1a;获取键盘输入引用类型一般使用步骤1、导包&#xff1a;impo…