学习设计模式之观察者模式,但是宝可梦

news2024/11/17 2:51:10

前言

作者在准备秋招中,学习设计模式,做点小笔记,用宝可梦为场景举例,有错误欢迎指出。

观察者模式

观察者模式定义了一种一对多的依赖关系,一个对象的状态改变,其他所有依赖者都会接收相应的通知。

所以,

  • 何时使用: 一个对象的状态改变,其他所有依赖对象都要知道
  • 意图: 定义一种一对多的依赖关系,一个对象状态改变,所有其他对象都要得到通知
  • 主要解决: 在降低耦合的基础上,实现一对多的协作

观察者模式中,存在四种角色:

  • 主题 (Subject): 被观察者,它要有能通知的状态,要维护一个观察者列表。
  • 观望者(Observer): 观察者,接收主题的通知。需要有一个更新方法,接收到主题状态改变后调用。
  • 具体主题(Concrete Subject): 主题的实现类。
  • 具体观察者(Concrete Observer): 观察者的实现类,实现更新方法。

是不是觉得,四种角色突然被引入,十分抽象?别急,看看下面的故事。

1. 情景模拟

现在宝可梦研究所业务如日中天,人人都想开个研究所狠狠地赚一笔。
但是想要开宝可梦研究所,那肯定得先把宝可梦研究透啊,怎么研究呢?先抓回来!
所以新开的小木博士研究所就打算把关都地区和城都地区的宝可梦都给抓来再说。
小木博士决定,雇点训练家来抓宝可梦,于是大量的训练家都来兼职。
作为亲属的小智和小茂也来兼职帮个忙,小智负责关都地区,小茂负责成都地区。

为了控制成本,每种宝可梦抓一只就够了,所以,每当一个训练家抓到一个精灵,就要通知一下其他同行:
某个地区已经抓住了多少个精灵啦,总共还剩多少个,以免大家抓到重复。
(当然,因为小智和小茂是家属,所以开点小灶,只通知了他俩。)
于是可以引入了观察者模式的各个对象:

  • 主题: 研究所的业务
  • 观察者: 训练家
  • 具体主题: 抓宝可梦的任务
  • 具体观察者: 负责不同地区的训练家

在这里插入图片描述

2. 代码

先随便定义一个主题的接口

public interface SubjectLab {
}

定义观察者的接口,即训练家,其中,观察者依赖他要观察的Subject类

/**
 * 训练家
 */
public abstract class ObserverTrainer {
    protected SubjectPokedex subject;
    public abstract void update();
}

小智和小茂分别负责各自地区

public class Satoshi extends ObserverTrainer{

    // 该对象观察这个Subject
    // 同时Subject也绑定这个对象,会发送通知
    public Satoshi(SubjectPokedex pokedex) {
        this.subject = pokedex;
        this.subject.employ(this);
    }

    @Override
    public void update() {
        System.out.println("I am Satoshi! I got the message:");
        this.subject.getDexState("Kanto");
    }
}

public class Shigeru extends ObserverTrainer{
    public Shigeru(SubjectPokedex subject) {
        this.subject = subject;
        this.subject.employ(this);
    }

    @Override
    public void update() {
        System.out.println("I am Shigeru! I got the message:");
        this.subject.getDexState("Johto");
    }
}

最后是这次抓宝可梦任务的实现类

public class SubjectPokedex implements SubjectLab{
    private List<ObserverTrainer> trainers = new ArrayList<>();
    private HashMap<String, Integer> pokedex = new HashMap<>();
    private Integer num;

    /**
     * 初始化,假设各个地区已经发现了很多宝可梦
     * 还有value个宝可梦没有被抓到研究所研究
     * num统计总数
     */
    public SubjectPokedex() {
        pokedex.put("Kanto", 155);
        pokedex.put("Johto", 155);
        num = 310;
    }

    /**
     * 打印某地区剩下的宝可梦
     * @param region 地区
     */
    public void getDexState(String region){
        System.out.println("There are still " + pokedex.getOrDefault(region, 0) + " Pokemons in " +
                region + " that haven't been captured.");
        System.out.println("There are a total of " + num +" Pokemon that have not been captured yet.\n");
    }

    /**
     * 抓获了num个宝可梦
     */
    public void catchPokemon(String region, Integer num){
        this.pokedex.put(region, pokedex.get(region) - num);
        this.num -= num;
        notifyAllTrainers();
    }

    /**
     * 任命愿意来帮助捕捉宝可梦的训练家们
     * @param trainer 训练家
     */
    public void employ(ObserverTrainer trainer){
        trainers.add(trainer);
    }

    /**
     * 通知所有参与的训练家
     */
    public void notifyAllTrainers(){
        for (ObserverTrainer trainer : trainers) {
            trainer.update();
        }
    }
}

测试类

public class ObserverDemo {
    public static void main(String[] args) {
        // Subject对象
        SubjectPokedex subjectPokedex = new SubjectPokedex();
        
        // 两个观察者,接收Subject的通知
        new Satoshi(subjectPokedex);
        new Shigeru(subjectPokedex);

        // 通知:关都地区抓住了30只
        subjectPokedex.catchPokemon("Kanto", 30);
        
        // 通知:城都地区抓住了90只
        subjectPokedex.catchPokemon("Johto", 90);
    }
}
I am Satoshi! I got the message:
There are still 125 Pokemons in Kanto that haven't been captured.
There are a total of 280 Pokemon that have not been captured yet.

I am Shigeru! I got the message:
There are still 155 Pokemons in Johto that haven't been captured.
There are a total of 280 Pokemon that have not been captured yet.

I am Satoshi! I got the message:
There are still 125 Pokemons in Kanto that haven't been captured.
There are a total of 190 Pokemon that have not been captured yet.

I am Shigeru! I got the message:
There are still 65 Pokemons in Johto that haven't been captured.
There are a total of 190 Pokemon that have not been captured yet.

3.应用

笔者水平有限,暂时不知道Java哪个常用API用到了观察者模式。
不过可以根据这个思想大概猜测,一种注册端口,再监听的做法和这个差不多。

如果你的微博关注了某人,TA发微博时会推送给你,关注这个行为就相当于是你开始观察这个人(Subject).
或者Steam心愿单添加了某款游戏,这游戏降价时也会推送给你,推送给每个观察者(添加愿望单的玩家).

4.和发布-订阅模式的讨论

有了解过消息队列的聪明的训练家就会感觉:这和发布-订阅模式很像啊!
在查阅资料的过程中,发现有些博主会把发布-订阅模式与观察者模式划等号,其实细品的话还是有一些区别。

我认为最大的区别在于,发布-订阅模式的主要目的是将发布者与订阅者解耦,发布者将消息发送给中间代理,然后代理分发消息给订阅者,让订阅者和发布者之间无需互相关注。
而观察者模式,强调的是观察者和被观察者之间的联系,被观察者(Subject)甚至会负责维护一个观察者的抽象类的列表,他们之间的联系是紧密的。

第二个区别,发布-订阅模式的消息传递是通过中间代理,而观察者模式是观察者与被观察者之间直接通信。

所以发布-订阅模式是不能跟观察者模式划等号的,观察者模式是一种更为简单的设计理念,而发布-订阅模式适用于更复杂的业务场景。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里云通用算力型u1云服务器CPU性能详细说明

​阿里云服务器u1是通用算力型云服务器&#xff0c;CPU采用2.5 GHz主频的Intel(R) Xeon(R) Platinum处理器&#xff0c;通用算力型u1云服务器不适用于游戏和高频交易等需要极致性能的应用场景及对业务性能一致性有强诉求的应用场景(比如业务HA场景主备机需要性能一致)&#xff…

外贸邮箱前缀选择指南:技巧揭秘!

外贸邮箱前缀用什么好&#xff1f;常见的外贸邮箱前缀有 zoho.com.cn&#xff0c; foxmail.com&#xff0c; hotmail.com&#xff0c; outlook.com&#xff0c; yahoo.com等等。那么这些前缀之间又有什么区别呢&#xff1f;选择哪个前缀对外贸业务更有利呢&#xff1f; 首先&am…

如何通过Side Effects来使得你使用Compose变的得心应手?

作者&#xff1a;clwater 虽然我使用Compose已经有了一段时间的, 但我还是觉得使用起来束手束脚的. 究其原因, 大概是coding时的思路还没有完全转换过来, 还没有沉浸在"Compose is Function"之中. 和我们熟悉的View不同, 当我们调用Compose之后, 我们就失去了它的修改…

代码审计-审计工具介绍-DAST+SAST+IAST项目

DASTSASTIAST项目介绍 DAST&#xff1a; 动态应用程序安全测试&#xff08;Dynamic Application Security Testing&#xff09;技术在测试或运行阶段分析应用程序的动态运行状态。它模拟黑客行为对应用程序进行动态攻击&#xff0c;分析应用程序的反应&#xff0c;从而确定该We…

*看门狗2_思考多组看门狗对多任务的监控

多任务系统中 一般结合嵌入式操作系统&#xff0c;设置一个优先级级别最高的任务作为监视器&#xff0c;以监视各个应用任务是否正常运行&#xff0c;该监视器即为软件看门狗&#xff0c;该任务对其他任务都设定一个计时器&#xff0c;每个被监视的任务在设定的时间内对软件看门…

企业级私有化部署即时通讯,完美替代SaaS平台

在数字化转型的时代&#xff0c;企业越来越需要安全、高效和可扩展的解决方案来管理其运营。作为通用办公行业的领军品牌&#xff0c;WorkPlus在企业级私有化部署领域成为改变者。凭借其尖端功能和全面的功能性&#xff0c;WorkPlus提供了传统的软件即服务&#xff08;SaaS&…

SELinux 入门 pt.1

哈喽大家好&#xff0c;我是咸鱼 文章《SELinux 导致 Keepalived 检测脚本无法执行》以【keepalived 无法执行检测脚本】为案例向大家简单介绍了关于 SELinux 的一些概念 比如说什么是自主访问控制 DAC 和 强制访问控制 MAC&#xff1b;SELinux 安全上下文的概念等等 那么今…

洛谷 P2715 ccj与zrz之在回家的路上 艰辛的解题过程

时隔将近两个月&#xff0c;我回来了哈哈哈哈哈哈哈哈哈。 看着一周小结的排名越来越低太难受了&#xff0c;于是我回来继续写文章 今天这道题其实不难&#xff0c;重点是说一下解题过程中遇到的麻烦和坑点 题目描述 给出一个算式&#xff08;a*bc或a/bc或abc或a-bc&#xff0…

深入理解JMM和并发三大特性

并发和并行 并发和并行的目的都是为了使CPU的使用率最大化&#xff0c;这两个概念也是我们容易混淆的。 并行&#xff08;Parallel&#xff09; 并行是指在同一时刻&#xff0c;有多条指令在多个处理器上同时执行&#xff0c;因为并行要求程序能同时执行多个操作&#xff0c…

APP上线为什么要提前部署安全产品呢?

一般平台刚上线或者日活跃量比较高的时候&#xff0c;很容易成为攻击者的目标&#xff0c;服务器如果遭遇黑客攻击&#xff0c;资源耗尽会导致平台无法访问&#xff0c;业务也无法正常开展&#xff0c;服务器一旦触发黑洞机制&#xff0c;就会被拉进黑洞很长一段时间&#xff0…

MySQL下载安装配置

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

微信聊天记录词云制作

文章目录 一&#xff1a;聊天记录传输至电脑二&#xff1a;聊天记录破解三&#xff1a;聊天记录分析&#xff08;1&#xff09;字段含义&#xff08;2&#xff09;词频统计和词云制作&#xff08;3&#xff09;效果展示 一&#xff1a;聊天记录传输至电脑 在雷电模拟器中打开r…

独家|2023 Q2业内3D-NAND研发动态最全一览表

有粉丝私信&#xff0c;希望可以看到业内比较全的3D-NAND动态。经过小编的找寻&#xff0c;结合TechInsight的信息以及各家原厂官网消息&#xff0c;整理出2023 Q2业内3D-NAND研发动态最全一览表&#xff0c;供大家参考。 声明&#xff1a;本表格均为公开信息整理&#xff0c;如…

AI提示语-让每个人都能轻松使用 AI,提高 10 倍生产力

AI提示语简介 无需代码&#xff0c;连接模型快速构建AI应用。让每个人都能轻松使用 AI&#xff0c;提高 10 倍生产力。 AI提示语介绍 AI 提示语致力于为各类AI应用搭建、运行和推广的基础平台&#xff0c;让广大用户都能将AI的力量运用到实际的工作与生活场景中。 AI提示语…

国产SSD崛起!2023年发展趋势解析

随着科技的不断进步&#xff0c;SSD已经成为了现代计算机不可或缺的一部分。而在这个市场中&#xff0c;国产SSD也逐渐崭露头角。 国产SSD市场份额继续扩大 根据市场调研机构IDC的数据显示&#xff0c;2023年中国SSD市场份额排名前五的厂商中有四家是国内企业&#xff0c;分别是…

【第三阶段】kotlin语言的内置函数apply

1.普通方式输出一个字串的信息 fun main() {var info"Ktolin"//普通方式println("info的长度为${info.length}")println("info的最后一个字符${info[info.length-1]}")println("info全转换为大写${info.toLowerCase()}") }执行结果 …

Go 语言在 Windows 上的安装及配置

1. Go语言的下载 Golang官网&#xff1a;All releases - The Go Programming Language Golang中文网&#xff1a;Go下载 - Go语言中文网 - Golang中文社区 两个网站打开的内容只有语言不同而已&#xff0c;网站上清晰的标注了不同操作系统需要对应安装哪个版本&#xff0c;其中…

对类加载过程的通俗理解

开局一张图 一般来说&#xff0c;类加载分为&#xff1a;加载、验证、准备、解析、初始化 5个步骤。 各阶段略叙 1、加载 将.class文件加载进内存 2、验证 判断.class文件的格式是否正确 3、准备 为类的静态变量分配内存并设置初始值。只有b、c会在此阶段进行处理。 //…

移动端的帮助中心该如何设计?

随着移动互联网的发展&#xff0c;APP作为一种高效的营销工具&#xff0c;已经成为企业抢占移动端流量的重要手段。同时随着智能手机用户规模不断扩大&#xff0c;以及用户的消费逐渐向移动端转移&#xff0c;使得越来越多企业认识到 APP与其营销模式是成为互补的关系&#xf…

数据驱动未来:基于状态的维修在工业领域的前景

随着科技的不断进步&#xff0c;工业界正迎来一场革命&#xff0c;而其中的一颗明星是基于状态的维修&#xff08;CBM&#xff09;。这一技术正在改变工业维护的方式&#xff0c;通过实时数据分析和人工智能驱动&#xff0c;使维护从被动的、计划性的转变为主动的、预测性的。本…