今天我们模拟小米智能家居应用场景,融合单例模式、组合模式、观察者模式做一个综合应用案例。具体场景如下:

  1. 小米门铃为主人开门,触发开门事件;
  2. 小米智能控制台监测到开门事件,自动启动小米空调和小米电视;

思路分析

  • 小米控制台,在家庭单位内只有一个实例,我们使用单例模式;
  • 小米控制台通过startWork这一统一接口控制全部智能家居,对于有相同接口的不同实例进行统一调度,我们可以应用组合模式;
  • 小米控制台监听小米门铃的开门事件,观察者模式;

定义【组合】和【组件】两个父类接口

后续由小米控制台对【组合】做具体实现,小米电视和小米空调对【组件】做具体实现

        /* 组合模式 【组合】父类定义 */class Compose {constructor (name){this.name = name// components 组件调度列表this.components = []}// 添加组件到调度列表addComponent(component){this.components.push(component)}// 轮询所有组件 令其开始工作startWork(){this.components.forEach(component=>{component.startWork()})}}/* 组合模式 【组件】父类定义 */class Component {// 接收组件名称constructor(name){this.name = name}// 所以组件统一调度接口 这里留白等待子类做具体实现startWork(){}}

定义小米控制台类、小米电视类、小米空调类

小米控制台类,通过继承【组合】父类,实现了添加设备和指挥所有设备开始工作

        /* 小米控制台 继承【组合】父类 实现添加组件、开始工作两个方法*/class XiaomiControl extends Compose {constructor (){// 调用父类方法为设备命名super("小米总控台")}}

定义小米空调和小米电视类

继承【组件】父类,实现统一调度接口,为后续控制台对它们做统一调度做好准备

        class XiaomiKongtiao extends Component {constructor(){super("小米空调")}// 对统一调度接口做具体实现startWork(){console.log(`${this.name}开始调节室温`);}}class XiaomiTV extends Component {constructor(){super("小米电视")}// 对统一调度接口做具体实现startWork(){console.log(`${this.name}自动切换到您喜欢的节目`);}}

设置小米控制台为全局唯一单例

好理解,一个家庭只需要一个控制台实例,通过getXmcSingleton()方法获取该实例

        // instance 实例 singleton 单例let xmcInstance = nullfunction getXmcSingleton() {xmcInstance = xmcInstance === null? new XiaomiControl() : xmcInstancereturn xmcInstance}

实现统一调度

        // 获取小米控制台实例// const xmControl = new XiaomiControl()const xmControl = getXmcSingleton()// 创建小米电视和小米空调实例const xmKongtiao = new XiaomiKongtiao()const xmTv = new XiaomiTV()// 添加电视和空调到调度列表xmControl.addComponent(xmKongtiao)xmControl.addComponent(xmTv)// 所有智能家居开始工作xmControl.startWork()

小米控制台控制所有智能设备开始工作的时机,为小米门铃开门的一刹那,所以接下来我们通过应用观察者模式实现这一需求;

定义被观察者(数据)和观察者两个父类

被观察者要实现注册观察者、注销观察者、改变数据状态、触发事件(所以观察者响应)四个接口

       /* 提供被观察数据的父类接口 */class Observable {constructor(){// 预备存储事件类型和对应的观察者列表this.typeListeners = {}// 预备存储事件类型和对应的值this.typeValue = {}}// 注册观察者addListener(type,...listeners){if(Object.prototype.hasOwnProperty.call(this.typeListeners,type)){this.typeListeners[type].push(...listeners)}else{this.typeListeners[type] = [...listeners]}}// 注销观察者removeListener(type,listener){if(Object.prototype.hasOwnProperty.call(this.typeListeners,type)){for(let i=this.typeListeners[type].length-1;i>=0;i--){const item = this.typeListeners[type][i]if(item === listener){this.typeListeners[type].splice(i,1)}}}}// 引起数据变化setValue(type,newValue){this.typeValue[type] = newValue// 数据变化触发事件this.trigger(type,newValue)}// 触发观察者响应trigger(type,newValue){if(Object.prototype.hasOwnProperty.call(this.typeListeners,type)){this.typeListeners[type].forEach(item=>{item.onEventHappen(type,newValue)})}}}

观察者接口主要需要提供事件响应接口的定义

        /* 观察者父类 */class Observer {// 事件响应统一接口,留白待子类做具体实现onEventHappen(type,newValue){}}

小米门铃实现【被观察者/数据】父类,通过继承获得注册设备、注销设备、改变数据和触发事件等具体功能;
在开关门时,会引起doorOpen事件的数据变化,从而触发观察者的响应动作

        /* 小米门铃 实现被观察数据接口 开门关门就是事件 */class XiaomiDoorbell extends Observable {// 开门时数据变化 父类帮你会【触发事件】(被观察者所提供的接口)open(){this.setValue("doorOpen",true)}close(){this.setValue("doorOpen",false)}}

小米控制台对观察者父类做具体实现,这里由于前面小米控制台已经继承过一遍【组合】这个父类,而在JavaScript中并不存在标准的多继承,所以这里我们只需要令其实现观察者的onEventHappen(type,value)接口,就能事实上令其成为一个具有观察者功能的类;

扩展后的小米控制台类代码如下:

        /* 小米控制台 继承【组合】父类 实现添加组件、开始工作两个方法*/class XiaomiControl extends Compose {constructor (){// 调用父类方法为设备命名super("小米总控台")}// 对【观察者】“父类”的事件响应接口做具体实现// 成为一个“事实上”的观察者onEventHappen(type,newValue){switch (type) {case "doorOpen":if(newValue === true){this.startWork()}break;default:break;}}}

OK,一切准备就绪,接下来创建门铃,添加小米控制台为观察者,并触发开门事件

        // 创建小米门铃实例const xmdb = new XiaomiDoorbell()// 添加小米控制台到doorOpen事件的观察者列表xmdb.addListener("doorOpen",xmControl)// 通过按钮、定时器等触发小米门铃开门,进而触发小米控制台响应并令所有设备开始工作// 这里简化一下xmdb.open()

完整代码如下:

    <script>/* 提供被观察数据的父类接口 */class Observable {constructor(){// 预备存储事件类型和对应的观察者列表this.typeListeners = {}// 预备存储事件类型和对应的值this.typeValue = {}}// 注册观察者addListener(type,...listeners){if(Object.prototype.hasOwnProperty.call(this.typeListeners,type)){this.typeListeners[type].push(...listeners)}else{this.typeListeners[type] = [...listeners]}}// 注销观察者removeListener(type,listener){if(Object.prototype.hasOwnProperty.call(this.typeListeners,type)){for(let i=this.typeListeners[type].length-1;i>=0;i--){const item = this.typeListeners[type][i]if(item === listener){this.typeListeners[type].splice(i,1)}}}}// 引起数据变化setValue(type,newValue){this.typeValue[type] = newValue// 数据变化触发事件this.trigger(type,newValue)}// 触发观察者响应trigger(type,newValue){if(Object.prototype.hasOwnProperty.call(this.typeListeners,type)){this.typeListeners[type].forEach(item=>{item.onEventHappen(type,newValue)})}}}/* 观察者父类 */class Observer {// 事件响应统一接口,留白待子类做具体实现onEventHappen(type,newValue){}}/* 组合模式 【组合】父类定义 */class Compose {constructor (name){this.name = name// components 组件调度列表this.components = []}// 添加组件到调度列表addComponent(component){this.components.push(component)}// 轮询所有组件 令其开始工作startWork(){this.components.forEach(component=>{component.startWork()})}}/* 组合模式 【组件】父类定义 */class Component {// 接收组件名称constructor(name){this.name = name}// 所以组件统一调度接口 这里留白等待子类做具体实现startWork(){}}// instance 实例 singleton 单例let xmcInstance = nullfunction getXmcSingleton() {xmcInstance = xmcInstance === null? new XiaomiControl() : xmcInstancereturn xmcInstance}/* 小米门铃 实现被观察数据接口 开门关门就是事件 */class XiaomiDoorbell extends Observable {// 开门时数据变化 父类帮你会【触发事件】(被观察者所提供的接口)open(){this.setValue("doorOpen",true)}close(){this.setValue("doorOpen",false)}}/* 小米控制台 继承【组合】父类 实现添加组件、开始工作两个方法*/class XiaomiControl extends Compose {constructor (){// 调用父类方法为设备命名super("小米总控台")}// 对【观察者】“父类”的事件响应接口做具体实现// 成为一个“事实上”的观察者onEventHappen(type,newValue){switch (type) {case "doorOpen":if(newValue === true){this.startWork()}break;default:break;}}}class XiaomiKongtiao extends Component {constructor(){super("小米空调")}// 对统一调度接口做具体实现startWork(){console.log(`${this.name}开始调节室温`);}}class XiaomiTV extends Component {constructor(){super("小米电视")}// 对统一调度接口做具体实现startWork(){console.log(`${this.name}自动切换到您喜欢的节目`);}}</script>

JavaScript设计模式综合应用案例相关推荐

  1. JavaScript设计模式系列四之外观模式(附案例源码)

    文章初衷 设计模式其实旨在解决语言本身存在的缺陷, 目前javaScript一些新的语法特性已经集成了一些设计模式的实现, 大家在写代码的时候,没必要为了用设计模式而去用设计模式, 那么我这边为什么还 ...

  2. Javascript设计模式-超详细笔记

    Javascript设计模式 什么是设计模式 1. 什么是设计模式 设计模式是前人总结出的,解决开发中某类问题的方法: 我们在过去的代码编写中已经接触过很多的设计模式了,只不过当时咱们不知道这就是一种 ...

  3. JavaScript 设计模式核⼼原理与应⽤实践 之 结构型设计模式

    JavaScript 设计模式核⼼原理与应⽤实践 之 结构型设计模式 结构型:装饰器模式--对象装上它,就像开了挂 装饰器模式,又名装饰者模式.它的定义是"在不改变原对象的基础上,通过对其进 ...

  4. educoder头歌Web实训 web课——综合应用案例:动态焦点图页面的制作

    educoder头歌Web实训 太原理工web课--综合应用案例:拼图页面的制作[全网更新最快]_玛卡巴卡的博客-CSDN博客 第1关:动态焦点图页面的样式设计 任务描述 本关任务: 完成动态焦点图 ...

  5. JavaScript设计模式系列—模式篇总结(上)

    转载请注明预见才能遇见的博客:http://my.csdn.net/ 原文地址:https://blog.csdn.net/pcaxb/article/details/102517956 JavaSc ...

  6. 15分钟带你了解前端工程师必知的javascript设计模式(附详细思维导图和源码)

    前言 设计模式是一个程序员进阶高级的必备技巧,也是评判一个工程师工作经验和能力的试金石.设计模式是程序员多年工作经验的凝练和总结,能更大限度的优化代码以及对已有代码的合理重构.作为一名合格的前端工程师 ...

  7. educoder头歌Web实训 web课——综合应用案例:限时秒杀效果的制作

    educoder头歌Web实训 太原理工web课--综合应用案例:动态焦点图页面的制作[全网更新最快]_玛卡巴卡的博客-CSDN博客 第1关:限时秒杀效果图片渲染 [TOC] 图1如下 链接为htt ...

  8. javascript 设计模式之观察者模式

    什么是观察者模式 观察者模式 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新. -- Graph ...

  9. JavaScript 设计模式核⼼原理与应⽤实践 之 行为型:策略模式——重构小能手,拆分“胖逻辑”

    JavaScript 设计模式核⼼原理与应⽤实践 之 行为型:策略模式--重构小能手,拆分"胖逻辑" 行为型:策略模式--重构小能手,拆分"胖逻辑" 策略模式和 ...

最新文章

  1. NOI2011 道路修建
  2. 使用VC++ 显示一首诗歌
  3. MySQL多表查询核心优化
  4. Linux进程终止命令kill或killall​笔记
  5. vue-router 如何在当前路由下重新点击当前路由的router-link实现刷新
  6. C语言之如何理解指针的指针(九)
  7. Dagger 2 系列(一) -- 前奏篇:依赖注入的基本介绍
  8. oracle数字进一函数,oracle常用函数一:数字函数
  9. 如何备份android,如何备份安卓手机系统
  10. 可禁用计算机服务,哪些Microsoft服务项目可以禁用以提高 电脑速度
  11. 《二十世纪西方思想文化潮流》笔记--导论5--理性的后果1
  12. 京东跨端组件库 NutUI 2.0 来袭
  13. Android视频加水印和压缩
  14. TencentOS学习笔记(1)
  15. 英语学习笔记(二)语法
  16. 洛谷 P1885 Moo
  17. 关于\xEF\xBB\xBF的介绍
  18. 计算机网络知识点总结(计网期末盲押系列)
  19. 谈谈favicon和他带来的问题
  20. PCA(主成分分析)的理解与应用(学习笔记)

热门文章

  1. parseInt鲜为人知的用法
  2. (4)复函数与拉普拉斯变换
  3. 怎么把桌面计算机放到快速启动栏,如何设置电脑快速启动?
  4. Python中calendar,time,datetime模块详情解 -------18
  5. 计算机屏幕灯,这样挑选电脑屏幕灯,同事看了都说靠谱!
  6. springboot实现敏感字段加密存储,解密显示
  7. 在配置Intel realsense (D435i)时遇到的问题
  8. Using getResponseBodyAsStream instead is recommended
  9. Linux编程定时执行某函数
  10. 实现一台电脑可上公司内网也可以访问外网