JavaScript设计模式综合应用案例

本文通过模拟小米智能家居场景,展示了如何结合单例模式、组合模式和观察者模式来实现设备的统一管理和事件响应。小米门铃开门事件触发控制台启动空调和电视,体现了这三个设计模式的巧妙运用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

  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 = null
        function getXmcSingleton() {
            xmcInstance = xmcInstance === null? new XiaomiControl() : xmcInstance
            return 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 = null
        function getXmcSingleton() {
            xmcInstance = xmcInstance === null? new XiaomiControl() : xmcInstance
            return 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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖的乔布梭

你好我是秦始皇转世,资助请从速

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值