【原创】自己动手循序渐进实现观察者模式

2024-03-10 13:38

本文主要是介绍【原创】自己动手循序渐进实现观察者模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

引言

自上一篇《自己动手实现牛逼的单例模式》问世之后,得到了不错的评价。于是博主在五一放弃出去游玩机会,赶制了这篇《自己动手循序渐进实现观察者模式》,依然还是一步一步推导出最终版的观察者模式。

观察者模式

定义:在许多设计中,经常涉及多个对象都对一个特殊对象中的数据变化感兴趣,而且这多个对象都希望跟踪那个特殊对象中的数据变化,在这样的情况下就可以使用观察者模式。
在这里,我们以母亲观察宝宝为例子,宝宝正在睡觉,醒来后,母亲做出相应的喂食行为。

1、观察者V1

假设我们用下图所示的情景

640?wx_fmt=jpeg


先定义一个Baby类,baby先睡五秒,五秒后醒来,代码如下所示

 

package rjzheng.observer1;public class Baby implements Runnable {    // 默认是睡着private boolean wakeup = false;    // 醒来的行为public void wakeUp() {        this.wakeup = true;}    public boolean isWakeup() {        return wakeup;}    public void setWakeup(boolean wakeup) {        this.wakeup = wakeup;}    @Overridepublic void run() {        // TODO Auto-generated method stubtry {Thread.sleep(5000);} catch (InterruptedException e) {            // TODO Auto-generated catch blocke.printStackTrace();}wakeUp();}
}

而母亲类即Mother类,则一直监听宝宝的状态,宝宝一醒来就喂食,代码如下所示

package rjzheng.observer1;public class Mother implements Runnable {    private Baby baby;    public Mother(Baby baby) {        this.baby = baby;}    public void feed(Baby baby){System.out.println("已经给宝贝喂食");}    @Overridepublic void run(){        while(!baby.isWakeup()){            for(int i=0;i<5;i++){                try {Thread.sleep(1000);System.out.println("宝宝还有"+(5-i)+"秒醒来");} catch (InterruptedException e) {                    // TODO Auto-generated catch blocke.printStackTrace();}}}        this.feed(baby);}
}

然后写一个ObserverTest1测试一下,代码如下所示

package rjzheng.observer1;public class ObserverTest1 {    public static void main(String[] args) {Baby baby =new Baby();        new Thread(baby).start();        new Thread(new Mother(baby)).start();}
}

输出如下

宝宝还有5秒醒来
宝宝还有4秒醒来
宝宝还有3秒醒来
宝宝还有2秒醒来
宝宝还有1秒醒来
已经给宝贝喂食

2、观察者V2

这时情景变为下图

640?wx_fmt=jpeg


可是上一版,母亲要一直看着宝宝,母亲觉得太累了,母亲要是想中间聊个微信啥的,多不方便,于是V2版出现了。我们能不能孩子醒来后,主动告诉给母亲呢。于是修改Baby的wakeUp方法,即醒来状态改变后,增加一个通知母亲的方法。
综上所述,Baby类做如下几点修改

 

  1. 持有Mother的引用,修改构造函数

  2. 在wakeUp方法中增加,通知母亲的逻辑

  3. 在run方法中,自己五秒后醒来,自己修改自己的状态

package rjzheng.observer2;public class Baby implements Runnable {    // 默认是睡着private boolean wakeup = false;    private Mother mother;    //1. 持有Mother的引用,修改构造函数public Baby(Mother mother){        this.mother = mother;}    //2. 在wakeUp方法中增加,通知母亲的逻辑public void wakeUp() {wakeup = true;        this.mother.feed(this);}    public boolean isWakeup() {        return wakeup;}    public void setWakeup(boolean wakeup) {        this.wakeup = wakeup;}    //3. 在run方法中,自己五秒后醒来,自己修改自己的状态@Overridepublic void run() {        while(!this.isWakeup()){            for(int i=0;i<5;i++){                try {Thread.sleep(1000);System.out.println("宝宝还有"+(5-i)+"秒醒来");} catch (InterruptedException e) {                    // TODO Auto-generated catch blocke.printStackTrace();}}            this.wakeUp();}}
}

而Mother类,则不需要做线程操作,Baby类通知Mother类后,做出相应处理即可。因此,Mother类修改如下

  1. 删除run方法等线程相关操作

  2. 增加一个无参构造方法(因为Mother类不需要持有Baby类的引用了)
    综上所述,Mother类的代码如下

package rjzheng.observer2;public class Mother{    private Baby baby;    public Mother(){}    public Mother(Baby baby) {        this.baby = baby;}    public void feed(Baby baby){System.out.println("已经给宝贝喂食");}
}

测试类ObserverTest2的代码如下

package rjzheng.observer2;public class ObserverTest2 {    public static void main(String[] args) {Baby baby =new Baby(new Mother());        new Thread(baby).start();}
}

输出如下所示

宝宝还有5秒醒来
宝宝还有4秒醒来
宝宝还有3秒醒来
宝宝还有2秒醒来
宝宝还有1秒醒来
已经给宝贝喂食

3、观察者V3

640?wx_fmt=jpeg

这个时候,宝宝的父亲回来了。父亲觉得,在宝宝醒来后,应该带宝宝出去玩。于是就和孩子他妈约定,根据孩子醒来的时间决定,带孩子做什么。如果醒来的时间是饭点,就给孩子喂食;如果醒来的时间不是饭点,就带孩子出去玩。
那么重点来了,醒来的时间是否为饭点这个属性,放在父亲类里,不大合适;放在宝宝类里也不大合适;放母亲类里也不大合适。根据面向对象的设计原则,我们定义一个醒来的事件类WakeUpEvent,将醒来的时间是否为饭点放在WakeUpEvent里。除此之外,再定义一个事件源source属性,那么WakeUpEvent类如下所示

package rjzheng.observer3;/*** 醒来的事件对象* @author zhengrongjun**/public class WakeUpEvent {    //醒来时间是否为饭点private boolean isFoodTime;    //事件源private Baby source;    public WakeUpEvent(boolean isFoodTime,Baby source){        this.isFoodTime = isFoodTime;        this.source = source;}    public boolean isFoodTime() {        return isFoodTime;}    public void setFoodTime(boolean isFoodTime) {        this.isFoodTime = isFoodTime;}    public Baby getSource() {        return source;}    public void setSource(Baby source) {        this.source = source;}
}

那么,接下来,Father类拥有一个带宝贝出去玩的方法,如下所示

package rjzheng.observer3;public class Father {    public void play(WakeUpEvent wakeUpEvent){        if(!wakeUpEvent.isFoodTime()){System.out.println("抱宝贝出去玩");}}
}

自然,Mother类拥有一个给宝贝喂食的方法,如下所示

package rjzheng.observer3;public class Mother{    public void feed(WakeUpEvent wakeUpEvent){        if(wakeUpEvent.isFoodTime()){System.out.println("给宝贝喂食");}}
}

那么在这种情况下Baby类修改为如下所示

package rjzheng.observer3;public class Baby implements Runnable {    private Mother mother;    private Father father;    public Baby(Mother mother,Father father) {        this.mother = mother;        this.father = father;}    public void wakeUp() {        this.mother.feed(new WakeUpEvent(true, this));        this.father.play(new WakeUpEvent(true, this));}    @Overridepublic void run() {        boolean flag = true;        while (flag) {            for (int i = 0; i < 5; i++) {                try {Thread.sleep(1000);System.out.println("宝宝还有" + (5 - i) + "秒醒来");} catch (InterruptedException e) {                    // TODO Auto-generated catch blocke.printStackTrace();}}            this.wakeUp();flag=false;}}
}

测试类ObserverTest3如下所示

package rjzheng.observer3;public class ObserverTest3 {    public static void main(String[] args) {Baby baby =new Baby(new Mother(),new Father());        new Thread(baby).start();}
}

运行结果如下

宝宝还有5秒醒来
宝宝还有4秒醒来
宝宝还有3秒醒来
宝宝还有2秒醒来
宝宝还有1秒醒来
给宝贝喂食

4、观察者V4

这时情景变为下图

640?wx_fmt=jpeg


又过了几天,孩子不乐意了。。。。孩子觉得,我每次还要去记爸爸能带我干嘛,妈妈能带我干嘛,太麻烦了。以后,爷爷奶奶来来了,我难道还要去记你们能干嘛,再通知相应的人嘛。不如这样,我把你们全部叫来,你们自己商量好,该带我干嘛吧。

 

OK。。这也是观察者V3版的缺点,代码复用性太差。如果多一个爷爷GrandFather类,那Baby类里头还要多一个Grandfather类的引用,并且在wakeup里增加通知逻辑,太麻烦。索性,定义一个WakeUpListener接口,继承这个接口的人,代表都具有处理WakeUpEvent事件的能力

那么WakeUpListener接口的源码如下

package rjzheng.observer4;public interface WakeUpListener {    public void actiontoWakenUp(WakeUpEvent wakeUpEvent);
}

这时Father类和Mother类代码如下,都实现了WakeUpListener接口

package rjzheng.observer4;public class Father implements WakeUpListener {    @Overridepublic void actiontoWakenUp(WakeUpEvent wakeUpEvent) {        // TODO Auto-generated method stubif(!wakeUpEvent.isFoodTime()){System.out.println("抱宝贝出去玩");} }
}
package rjzheng.observer4;public class Mother implements WakeUpListener{    @Overridepublic void actiontoWakenUp(WakeUpEvent wakeUpEvent) {        if(wakeUpEvent.isFoodTime()){System.out.println("给宝贝喂食");}}
}

这时的Baby类如下所示

package rjzheng.observer4;import java.util.ArrayList;import java.util.List;public class Baby implements Runnable {    private List<WakeUpListener> wakeUpListeners = new ArrayList(); public void addListeners(WakeUpListener wakeUpListener){        this.wakeUpListeners.add(wakeUpListener);}    public void wakeUp() {        for(WakeUpListener listener : wakeUpListeners)listener.actiontoWakenUp(new WakeUpEvent(true, this));}    @Overridepublic void run() {        boolean flag = true;        while (flag) {            for (int i = 0; i < 5; i++) {                try {Thread.sleep(1000);System.out.println("宝宝还有" + (5 - i) + "秒醒来");} catch (InterruptedException e) {                    // TODO Auto-generated catch blocke.printStackTrace();}}            this.wakeUp();flag=false;}}
}

测试类ObserverTest4为

package rjzheng.observer4;public class ObserverTest4 {    public static void main(String[] args) {Baby baby =new Baby();baby.addListeners(new Father());baby.addListeners(new Mother());        new Thread(baby).start();}
}

最后结果如下

宝宝还有5秒醒来
宝宝还有4秒醒来
宝宝还有3秒醒来
宝宝还有2秒醒来
宝宝还有1秒醒来
给宝贝喂食

总结

本篇文章给出了四种观察者模式,一般都是用V4版啦。。前面V1,V2,V3都只是铺垫,为了引出最后一版的观察者模式,希望大家有所收获。

 

对CSDNJava架构技术,对架构技术感兴趣的同学,欢迎加QQ群859729143,一起学习,相互讨论。

群内已经有小伙伴将知识体系整理好(源码,笔记,PPT,学习视频),欢迎?加群免费领取

分享给喜欢Java,喜欢编程,有梦想成为架构师的程序员们,希望能够帮助到你们。

 

这篇关于【原创】自己动手循序渐进实现观察者模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

C#之List集合去重复对象的实现方法

《C#之List集合去重复对象的实现方法》:本文主要介绍C#之List集合去重复对象的实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C# List集合去重复对象方法1、测试数据2、测试数据3、知识点补充总结C# List集合去重复对象方法1、测试数据

Linux实现线程同步的多种方式汇总

《Linux实现线程同步的多种方式汇总》本文详细介绍了Linux下线程同步的多种方法,包括互斥锁、自旋锁、信号量以及它们的使用示例,通过这些同步机制,可以解决线程安全问题,防止资源竞争导致的错误,示例... 目录什么是线程同步?一、互斥锁(单人洗手间规则)适用场景:特点:二、条件变量(咖啡厅取餐系统)工作流

SpringBoot读取ZooKeeper(ZK)属性的方法实现

《SpringBoot读取ZooKeeper(ZK)属性的方法实现》本文主要介绍了SpringBoot读取ZooKeeper(ZK)属性的方法实现,强调使用@ConfigurationProperti... 目录1. 在配置文件中定义 ZK 属性application.propertiesapplicati

Java Multimap实现类与操作的具体示例

《JavaMultimap实现类与操作的具体示例》Multimap出现在Google的Guava库中,它为Java提供了更加灵活的集合操作,:本文主要介绍JavaMultimap实现类与操作的... 目录一、Multimap 概述Multimap 主要特点:二、Multimap 实现类1. ListMult

C#实现将Office文档(Word/Excel/PDF/PPT)转为Markdown格式

《C#实现将Office文档(Word/Excel/PDF/PPT)转为Markdown格式》Markdown凭借简洁的语法、优良的可读性,以及对版本控制系统的高度兼容性,逐渐成为最受欢迎的文档格式... 目录为什么要将文档转换为 Markdown 格式使用工具将 Word 文档转换为 Markdown(.

Java反射实现多属性去重与分组功能

《Java反射实现多属性去重与分组功能》在Java开发中,​​List是一种非常常用的数据结构,通常我们会遇到这样的问题:如何处理​​List​​​中的相同字段?无论是去重还是分组,合理的操作可以提高... 目录一、开发环境与基础组件准备1.环境配置:2. 代码结构说明:二、基础反射工具:BeanUtils

使用Python实现base64字符串与图片互转的详细步骤

《使用Python实现base64字符串与图片互转的详细步骤》要将一个Base64编码的字符串转换为图片文件并保存下来,可以使用Python的base64模块来实现,这一过程包括解码Base64字符串... 目录1. 图片编码为 Base64 字符串2. Base64 字符串解码为图片文件3. 示例使用注意

使用Python实现获取屏幕像素颜色值

《使用Python实现获取屏幕像素颜色值》这篇文章主要为大家详细介绍了如何使用Python实现获取屏幕像素颜色值,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、一个小工具,按住F10键,颜色值会跟着显示。完整代码import tkinter as tkimport pyau

在Java中将XLS转换为XLSX的实现方案

《在Java中将XLS转换为XLSX的实现方案》在本文中,我们将探讨传统ExcelXLS格式与现代XLSX格式的结构差异,并为Java开发者提供转换方案,通过了解底层原理、性能优势及实用工具,您将掌握... 目录为什么升级XLS到XLSX值得投入?实际转换过程解析推荐技术方案对比Apache POI实现编程

IDEA如何实现远程断点调试jar包

《IDEA如何实现远程断点调试jar包》:本文主要介绍IDEA如何实现远程断点调试jar包的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录问题步骤总结问题以jar包的形式运行Spring Boot项目时报错,但是在IDEA开发环境javascript下编译