JAVA SCRIPT设计模式--行为型--设计模式之Observer观察者模式(19)
创始人
2024-03-28 09:29:33

          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

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();//效果代码endthis.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




测试结果:

六、源代码下载

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

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...