JAVA SCRIPT设计模式--行为型--设计模式之Observer观察者模式(19)

news2024/11/24 6:25:20

          JAVA SCRIPT设计模式是本人根据GOF的设计模式写的博客记录。使用JAVA SCRIPT语言来实现主体功能,所以不可能像C++,JAVA等面向对象语言一样严谨,大部分程序都附上了JAVA SCRIPT代码,代码只是实现了设计模式的主体功能,不代表全部的正确,特此声明。若读者需要了解设计模式目录、原则、设计变化方向,环境相关等信息请查看设计模式开篇。


一、UML类图

参与者:

1.1 Subject(目标) 

  • 目标知道它的观察者。可以有任意多个观察者观察同一个目标。 
  • 提供注册和删除观察者对象的接口。

1.2 Observer(观察者)

  • 为那些在目标发生改变时需获得通知的对象定义一个更新接口。

1.3 ConcreteSubject(具体目标) 

  • 将有关状态存入各ConcreteObserver对象。 
  • 当它的状态发生改变时,向它的各个观察者发出通知。 

1.4 ConcreteObserver(具体观察者) 

  • 维护一个指向ConcreteSubject对象的引用。 
  • 存储有关状态,这些状态应与目标的状态保持一致。
  • 实现Observer的更新接口以使自身状态与目标的状态保持一致。

1.5 协作

二、意图

      定义对象间的一种一对多的依赖关系 ,当一个对象的状态发生改变时 , 所有依赖于它的对象都得到通知并被自动更新。

三、适用性 

  1. 当一个抽象模型有两个方面 , 其中一个方面依赖于另一方面。将这二者封装在独立的对 象中以使它们可以各自独立地改变和复用。
  2. 当对一个对象的改变需要同时改变其它对象 , 而不知道具体有多少对象有待改变。
  3. 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之 , 你不希望这些 对象是紧密耦合的。

四、优点和缺点

  1. 目标和观察者间的抽象耦合
            一个目标所知道的仅仅是它有一系列观察者 , 每个都符合抽象的Observer类的简单接口。目标不知道任何一个观察者属于哪一个具体的类。这样目标和观察者之间的耦合是抽象的和最小的。 因为目标和观察者不是紧密耦合的 , 它们可以属于一个系统中的不同抽象层次。一个处于 较低层次的目标对象可与一个处于较高层次的观察者通信并通知它 , 这样就保持了系统层次的 完整。如果目标和观察者混在一块 , 那么得到的对象要么横贯两个层次 (违反了层次性 ), 要么 必须放在这两层的某一层中 (这可能会损害层次抽象)。
  2. 支持广播通信
             不像通常的请求, 目标发送的通知不需指定它的接收者。通知被自动广 播给所有已向该目标对象登记的有关对象。目标对象并不关心到底有多少对象对自己感兴趣 ; 它唯一的责任就是通知它的各观察者。这给了你在任何时刻增加和删除观察者的自由。处理 还是忽略一个通知取决于观察者。
  3. 意外的更新
            因为一个观察者并不知道其它观察者的存在 , 它可能对改变目标的最终代 价一无所知。在目标上一个看似无害的的操作可能会引起一系列对观察者以及依赖于这些观 察者的那些对象的更新。此外 , 如果依赖准则的定义或维护不当,常常会引起错误的更新 , 这种错误通常很难捕捉。

五、示例代码

5.1  动机

        将一个系统分割成一系列相互协作的类有一个常见的副作用:需要维护相关对象间的一 致性。我们不希望为了维持一致性而使各类紧密耦合,因为这样降低了它们的可重用性。

        例如, 许多图形用户界面工具箱将用户应用的界面表示与底下的应用数据分离。定义应用数据的类和负责界面表示的类可以各自独立地复用。一个表格对象和一个柱状图对象可使用不同的表示形式描述同一个应用 数据对象的信息。表格对象和柱状图对象互相并不知道对方的存在,这样使你可以根据需要 单独复用表格或柱状图。但在这里是它们表现的似乎互相知道。当用户改变表格中的信息时 , 柱状图能立即反映这一变化 , 反过来也是如此。

        这一行为意味着表格对象和棒状图对象都依赖于数据对象 , 因此数据对象的任何状态改变都应立即通知它们。同时也没有理由将依赖于该数据对象的对象的数目限定为两个, 对相同的数据可以有任意数目的不同用户界面。

​        Observer模式描述了如何建立这种关系。这一模式中的关键对象是目标(subject)和观察者(observer)。一个目标可以有任意数目的依赖它的观察者。一旦目标的状态发生改变,所有的
观察者都得到通知。作为对这个通知的响应,每个观察者都将查询目标以使其状态与目标的状态同步。

        这种交互也称为发布-订阅(publish-subscribe)。目标是通知的发布者。它发出通知时并不需知道谁是它的观察者。可以有任意数目的观察者订阅并接收通知。

5.2  目录结构:

5.3 Subject(目标) 

  • 目标知道它的观察者。可以有任意多个观察者观察同一个目标。 
  • 提供注册和删除观察者对象的接口。

export default  class Subject {
    listObservers=[];
	 
    constructor( ) {
		 
    }
    
	Attach(observers)
	{
		this.listObservers.push(observers)
	}
	
	Notify()
	 {
		 for(let n=0;n<this.listObservers.length;n++)
		 {
		 	let item=this.listObservers[n];
		 	item.Update(this);
		 }
	 }
	
	 
  }

5.4 Observer(观察者)

  • 为那些在目标发生改变时需获得通知的对象定义一个更新接口。

export default  class Observer {
    
    constructor( ) {
		 
    }
    
	Update(subject)
	{
		  console.log(`Observer 继承次类,重写此类方法,订阅者获取到消息`);
	}
	
	 
  }

5.5 ConcreteSubject(具体目标) 

  • 将有关状态存入各ConcreteObserver对象。 
  • 当它的状态发生改变时,向它的各个观察者发出通知。 
import Subject  from './Subject.js'; 

export default  class DataModel extends Subject {
    a;
	b;
	c;
    constructor( ) {
		super();
    }
    SetValue(a,b,c)
	{
		this.a=a;
		this.b=b;
		this.c=c;
		super.Notify();
	}
 
	GetValue()
	{
		return {a:this.a,b:this.b,c:this.c}
	}
	
	 
  }

5.6 ConcreteObserver(具体观察者) 

  • 维护一个指向ConcreteSubject对象的引用。 
  • 存储有关状态,这些状态应与目标的状态保持一致。
  • 实现Observer的更新接口以使自身状态与目标的状态保持一致。
import Observer  from '../OneDataModel/Observer.js';

export default  class Widge extends Observer{
	ctx;
	rect;
    constructor(ctx,rect ) {
		 super();
		this.ctx=ctx;
		this.rect=rect;
    }
  
	Draw()
	{
		
	}

}
import Widget from '../Widget.js';
import Observer from '../../OneDataModel/Observer.js';


export default class BarShow extends Widget {
	barValue;
	constructor(ctx, rect) {
		super(ctx, rect);
	}
	Draw() {
		this.ctx.clearRect(this.rect.startx, this.rect.starty, this.rect.width, this.rect.height);
		this.ctx.lineWidth = 1;
		this.ctx.strokeRect(this.rect.startx, this.rect.starty, this.rect.width, this.rect.height);
		let x = this.rect.startx;
		let y = this.rect.starty;
		//以下效果代码
		this.ctx.beginPath();
		this.ctx.lineWidth = "4";
		this.ctx.fillStyle = '#' + Math.floor(Math.random() * 0xffffff).toString(16);
		this.ctx.fillRect(x + 20, y + 150, 20, -this.barValue.a);
		this.ctx.fillStyle = '#' + Math.floor(Math.random() * 0xffffff).toString(16);
		this.ctx.fillRect(x + 60, y + 150, 20, -this.barValue.b);
		this.ctx.fillStyle = '#' + Math.floor(Math.random() * 0xffffff).toString(16);
		this.ctx.fillRect(x + 100, y + 150, 20, -this.barValue.c);
		this.ctx.stroke();

		this.ctx.font="12px Arial";
		this.ctx.fillText(`BarShow a:` + this.barValue.a + `%  b:` + this.barValue.b + `%  c:` + this.barValue.c+ `%`,x+10,this.rect.starty+this.rect.width-30);
		//效果代码end
	}

	Update(subject) {
		this.barValue = subject.GetValue();
		console.log(`BarShow a:` + this.barValue.a + ` b:` + this.barValue.b + ` c:` + this.barValue.c);
		this.Draw();
	}

}
import Widget from '../Widget.js';
import Observer from '../../OneDataModel/Observer.js';
export default class PieShow extends Widget {
	value;
	constructor(ctx, rect) {
		super(ctx, rect);

	}
	Draw() {
		this.ctx.clearRect(this.rect.startx, this.rect.starty, this.rect.width, this.rect.height);
		this.ctx.lineWidth = 1;
		this.ctx.strokeRect(this.rect.startx, this.rect.starty, this.rect.width, this.rect.height);
		let x = this.rect.startx;
		let y = this.rect.starty;
		let aPer = this.value.a / 100;
		let bPer = this.value.b / 100;
		let cPer = this.value.c / 100;

		//以下效果代码

		let start = 0;
		let end = aPer;
		//产生随机颜色
		this.ctx.beginPath();
		this.ctx.fillStyle = '#' + Math.floor(Math.random() * 0xffffff).toString(16);
		this.ctx.moveTo(x + 100, y + 100);
		this.ctx.arc(x + 100, y + 100, 50, start * Math.PI * 2, end * Math.PI * 2)
		this.ctx.fill();
		this.ctx.stroke();
		
		start = end;
		end=aPer+bPer;
		this.ctx.beginPath();
		this.ctx.fillStyle = '#' + Math.floor(Math.random() * 0xffffff).toString(16);
		this.ctx.moveTo(x + 100, y + 100);
		this.ctx.arc(x + 100, y + 100, 50, start * Math.PI * 2, end * Math.PI * 2)
		this.ctx.fill();
		this.ctx.stroke();

		 start = end;
		 end=aPer+bPer+cPer;
		 this.ctx.beginPath();
		 this.ctx.fillStyle = '#' + Math.floor(Math.random() * 0xffffff).toString(16);
		 this.ctx.moveTo(x + 100, y + 100);
		 this.ctx.arc(x + 100, y + 100, 50, start * Math.PI * 2, end * Math.PI * 2)
		 this.ctx.fill();
		 this.ctx.stroke();

		//效果代码end
		this.ctx.font = "12px Arial";
		this.ctx.fillText(`PieShow a:` + this.value.a + `% b:` + this.value.b + `% c:` + this.value.c+ `%`, x + 10, this
			.rect.starty + this.rect.width - 30);
	}

	Update(subject) {
		this.value = subject.GetValue();
		console.log(`PieShow a:` + this.value.a + ` b:` + this.value.b + ` c:` + this.value.c);
		this.Draw();
	}
}

5.7  Client

import DataModel  from './OneDataModel/DataModel.js';
import BarShow  from './Widget/impl/BarShow.js'; 
import PieShow  from './Widget/impl/PieShow.js'; 
 
 

export default class Client{
     
    constructor(ctx,zooRect) {
    	let dataModel=new DataModel();
		
		let barShow=new BarShow(ctx,{startx:50,starty:50,width:200,height:200});
		dataModel.Attach(barShow);
		
		let pieShow=new PieShow(ctx,{startx:350,starty:50,width:200,height:200});
		dataModel.Attach(pieShow);
		
		dataModel.SetValue(50,30,20);
		
		/**模拟等待三秒后,实际调用**/
		setInterval(() => { 	 
			 console.log(` 定时更新 dataModel`);
			 let end=true;
			 let a,b,c;
			 do{
				  a=Math.floor(Math.random()*(100-1)+1);
				  b=Math.floor(Math.random()*(100-1)+1);
				  c=100-a-b;
				if(c>0) end=false;
			 }while(end)
			
			 dataModel.SetValue(a,b,c);
		}	, 3000 )
		
    }
	 
    
 }

5.8 测试HTML

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">


		<script type="module">
			import Client from './Client.js';
			var canvas = document.getElementById("mycanvas")
			var ctx = canvas.getContext("2d") //create 2d object
			let cl = new Client(ctx,{startx:0,starty:0,width:900,height:900});
		</script>
	</head>
	<body>
		<canvas id="mycanvas" width=900px height=900px></canvas>

	</body>
</html>

测试结果:

六、源代码下载

        下载链接:https://pan.baidu.com/s/1XuPqp84cccBNVkbnMY3sKw 
         提取码:q2ut

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

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

相关文章

Python图像识别实战(一):实现按比例随机抽取图像移动到另一文件夹

前面我介绍了可视化的一些方法以及机器学习在预测方面的应用&#xff0c;分为分类问题&#xff08;预测值是离散型&#xff09;和回归问题&#xff08;预测值是连续型&#xff09;&#xff08;具体见之前的文章&#xff09;。 从本期开始&#xff0c;我将做一个关于图像识别的…

Nacos集群搭建

1、下载nacos http://t.csdn.cn/ejfu9 2、配置Nacos 进入nacos的conf目录&#xff0c;修改配置文件cluster.conf.example&#xff0c;重命名为cluster.conf&#xff1a; 然后添加内容&#xff1a; 添加的内容是你要启动的多台nacos的IP和端口 127.0.0.1:8845 127.0.0.1:8846…

如何批量注册推特账号

Twitter推特账号怎么注册&#xff1f;相信国内好多朋友都被推特注册卡住&#xff0c;不知怎么注册twitter账号&#xff0c;由于国内限制的问题&#xff0c;推特账号注册比以前更麻烦了&#xff0c;本文将详细讲解Twitter怎么注册&#xff0c;Twitter (推特)是一个广受欢迎的社交…

【C#基础学习】第十五章、结构

目录 结构 1.结构的构造函数 1.1 实例构造函数 1.2 静态构造函数 1.3 总结 2.结构体作为返回值和参数 结构 结构的定义&#xff1a;结构是一种可以由程序员自定义的密封的值类型。 结构与类的区别&#xff1a;结构与类类似&#xff0c;它们都有自己的数据成员和函数成员。…

Nginx篇之实现反向代理和端口转发

一、前言 在正式生产环境中&#xff0c;web服务器、反向代理服务器的选择大都会选择nginx&#xff0c;确实&#xff0c;在常见的高并发场景下&#xff0c;nginx能够支持以万为单位的并发请求量&#xff0c;并且服务性能稳定&#xff0c;应用极为广泛。 二、反向代理含义 反向代…

【LeetCode_字符串_中心扩散 】5. 最长回文子串

目录考察点第一次&#xff1a;2022年12月8日10:29:05解题思路代码展示&#xff1a;中心扩散题目描述5. 最长回文子串 给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 示例 1&#xff1a; 输入&#xff1a;s "babad" 输出&#xff1a;"bab" 解…

高通平台开发系列讲解(Camera篇)新增GC8034摄像头步骤

文章目录 一、新增配置文件二、配置摄像头三、设置效果文件四、修改设备树五、修改用户空间驱动程序沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要介绍高通平台新增摄像头步骤。 一、新增配置文件 在vendor/qcom/proprietary/common/config/device-vendor.…

一文读懂数据加密

文章目录本文前言一、可逆加密1.1 对称加密&#xff08;传统加密算法&#xff09;1.2 非对称加密&#xff08;现代加密算法&#xff09;二、不可逆加密三、 混合加密、消息摘要和数字签名四、文章最后本文前言 在计算机信息安全领域&#xff0c;之前软件设计师的网络安全部分了…

解决Elasticsearch Connection reset by peer异常

一、问题现象 随着ES的密集使用&#xff0c;线上环境&#xff0c;不同应用最近几天陆续有报java.io.IOException: Connection reset by peer异常&#xff0c;感觉不太正常。直接影响就是用户查询或者变更ES数据失败。 java.io.IOException: Connection reset by peerat org.e…

大数据:Storm集成HDFS和HBase

一、Storm集成HDFS 1.1 项目结构 1.2 项目主要依赖 项目主要依赖如下&#xff0c;有两个地方需要注意&#xff1a; 这里由于我服务器上安装的是 CDH 版本的 Hadoop&#xff0c;在导入依赖时引入的也是 CDH 版本的依赖&#xff0c;需要使用 <repository> 标签指定 CDH …

自适应且不可删除的水印蒙层

目录 canvas自适应文字长度&#xff0c;旋转角度生成水印背景图 生成蒙层 禁止蒙层的删除和修改 canvas自适应文字长度&#xff0c;旋转角度生成水印背景图 设置canvas字体大小后&#xff0c;通过ctx.measureText(text).width获取两行文字的宽度text1&#xff0c;text2&…

python-(6-5-1)爬虫---xpath解析实战

文章目录一 环境准备二 需求三 分析1 拿到页面源代码2 提取和解析数据四 步骤流程1 拿到页面源代码2 提取和解析数据五 完整代码xpath是在XML文档中搜索内容的一门语言 html是xml的一个子集 一 环境准备 安装lxml模块 二 需求 爬取某网站的数据 三 分析 1 拿到页面源代码 …

计算机领域热知识【2】消息队列与celery

Celery是实现消息队列的一个工具&#xff0c;本篇博客将介绍消息队列的基础知识&#xff0c;以及celery实现消息队列的总体方法。想要实现用Celery实现消息队列实例的朋友&#xff0c;可以从本篇博客中找到我写的另一篇介绍使用Celery和RabbitMQ实现消息队列的博客。 目录消息队…

Java+Swing+mysql天气信息管理系统

JavaSwingmysql天气信息管理系统一、系统介绍二、功能展示1.主要功能2.主页3.查询历史天气三、数据库四、其他系统一、系统介绍 该系统实现: 通过高德天气API查询天气数据 将查询的数据存入本地数据库 删除数据。 二、功能展示 1.主要功能 2.主页 3.查询历史天气 三、数据库…

Netty04——优化与源码

目录1. 优化1.1 扩展序列化算法1.2 参数调优1.2.1.CONNECT_TIMEOUT_MILLIS1.2.2.SO_BACKLOG1.2.3.ulimit -n1.2.4.TCP_NODELAY1.2.5.SO_SNDBUF & SO_RCVBUF1.2.6.ALLOCATOR1.2.7.RCVBUF_ALLOCATOR1.3 RPC 框架1.3.1.准备工作1.3.2.服务器 handler1.3.3.客户端代码第一版1.3…

C#车库信息管理系统

车库信息管理系统实现 技术 C# sqlserver 系统功能 基本的登录注册车库管理员进行车辆信息的添加&#xff0c;即给车库登记车辆信息管理员对登记信息进行修改管理员对登记信息进行删除管理员对登记信息进行查询管理员对登记的车辆进行进库&#xff0c;出库处理实时统计车库…

CS61A学习笔记 lecture1 Computer science

CS61A学习笔记 lecture1 Computer science SICP: Structure and Interpretation of Computer Programs 计算机程序的构造和解释 一开始其实是想做南大的SICP学习笔记的&#xff0c;但是没有找到南大这门课的视频&#xff0c;还有就是他是CS61A的clone&#xff0c;网上也有CS61A…

Qt 多线程之QtConcurrent::map(处理序列容器)

QtConcurrent::map()、QtConcurrent::mapped() 和 QtConcurrent::mappedReduced() 函数对一个序列中&#xff08;例如&#xff1a;QList、QVector&#xff09;的项目并行地进行计算。 1、map函数 map函数的功能是在其他线程运行指定的函数&#xff0c;map函数有两个参数 第一…

耗时一个月整理的,最新出炉的Java面试题合集(2022亲身经历)

面试题清单 个人近来面试了不少的公司的&#xff0c;该挂的挂&#xff0c;该应付通过的应付通过&#xff0c;目前对面试题部分做一个系统的总结。最起码要保证被问过的问题第二次被问到的时候是可以回答并且理解的。算是一个被动输入学习的过程。 题目持续更新&#xff0c;答…

xdma linux 驱动

一、下载XDMA文件 输入命令: sudo git clone https://github.com/Xilinx/dma_ip_drivers cd xx_dma/dma_ip_drivers/XDMA/linux-kernel/xdma$ 二 、编译: sudo make install 在最后会遇到下面这个问题: 三、添加key 文件 cd /lib/modules/5.4.0-135-generic/build/ce…