js设计模式(五)发布订阅模式、组合模式、模板方法模、UI组件一般构建过程、职责链模式

一、发布订阅模式

定义:又称观察者模式,他定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于他的对象都将都到统治

        let event = {
            clientList:{},
            listen:function(key,fn){
                if(!this.clientList[key]){
                    this.clientList[key] = []
                }
                this.clientList[key].push(fn)
            },
            trigger:function(){
                let key = Array.prototype.shift.call(arguments)
                let fns = this.clientList[key]
                if(!fns||fns.length){ 
                    return false
                }
                for(let i = 0,fn;fn=fns[i++]){
                    fn.apply(this,arguments)
                }
            }
            
        }


        event.remove = function(key,fn){
            let fns = this.clientList[key]
            if(!fns){
                return false
            }
            if(!fn){
                fns&&((fns.length=0))//如果没有传入具体的回调函数,表示需要取消key对应消息的所有订阅
            }else{
                for(let l= fns.length;l>=0;l++){
                    let _fn = fns[1]
                    if(_fn === fn){
                        fns.splice(l,1)
                    }
                }
            }
        }
        let installEvent = function(obj){ //给对象动态安装发布订阅功能
            for(let i in obj){
                installEvent[i] = obj[i]
            }
        }

二、组合模式

组合模式将对象组合成树形结构,以表示"部分-整体"的层次结构,除了用来表示树形结构之外,组合模式的另一个好处是通过对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性

①表示属性结构:提供了一种便利树形结构的方案,通过调用组合的execute方法,程序会递归调用组合对象下面的也对象的execute方法,所以我们只需要一次操作即可完成几件事。组合模式可以方便的描述对象部分--整体层次结构。

②利用对象多态性同意对待组合对象和单个对象。利用对象的多态性表现,可以使客户端忽略组合对象和单个对象的不同。

在实际的开发中,这会带来极大的便利,当向组合模式里添加命令时,只要它是一个命令且有可执行的execute方法,那么这个命令就可以被添加进万能遥控器。

宏命令是一组命令的集合,通过执行宏命令的方式,可以一次执行一批命令。

       let MacroCommond = function(){
            return {
                commandList:[],
                add:function(command){
                    this.commandList.push(command)
                },
                execute:function(){
                    for(let i=0,command;command = this.commandList[i++];){
                        command.execute()
                    }
                }
            }
        }
        let openAcCommand = {
            execute:function(){
                console.log('打开空调')
            }
        }
        let openTvCommand = {
            execute:function(){
                console.log('打开电视')
            }
        }
        let macroCommand1 = MacroCommond()
        macroCommand1.add(openAcCommand)
        macroCommand1.add(openTvCommand)
        macroCommand1.execute()

宏命令可以包含宏命令组成一个"超级命令"

Folder.prototype.add = function(file){
            file.parent = this
            this.files.push(file)  //父节点引用
        }
        Folder.prototype.scan = function(){
            console.log('开始扫描文件'+this.name)
            for(let i=0,file,files=this.files;file = files[i++];){
                file.scan()
            }
        }
        Folder.prototype.remove = function(){
            if(!this.parent){
                return
            }
            for(let files = this.parent.files,l=files.length-1;l>=0;l--){
                let file = files[l]
                if(file===this){
                    files.splice(l,1)
                }
            }
        }

三、模板方法模式

模板方法模式是一种只需要使用继承就可以实现的非常简单的模式。

模板模式是一种典型的通过封装变化提高系统拓展性的设计模式。

模板方法由两部分组成,第一部分是抽象父类,第二部分是具体的实现子类。通常在抽象父类中封装了子类的算法框架,也包括一些公共方法以及封装在子类中所有方法的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。

Ⅰ、js没有抽象类的缺点和解决方案

缺点:当使用原型继承来模拟传统的类式继承时,并没有编译器帮助我们进行任何形式的检查,并不能保证子类会重写父类中的“抽象方法”

解决方案:

1、使用鸭子类型来模拟接口检查,但会带来不必要的复杂性

2、让Beverage等方法直接抛出一个异常,如果粗心忘记编写,在程序运行时会报错

 

Ⅱ、UI组件一般构建过程

①初始化一个div容器哦

②通过ajax请求拉取相应的容器哦

③数据渲染到div容器里面,完成组件的构造

④通知用户组建渲染完成

于是我们可以把这4个步骤都抽象到父类的模板方法里面,父类还以顺便提供第一步和第四步的具体实现。当子类继承这个父类后,会重写模板方法里的第二步和第三步

Ⅲ、钩子方法

放置钩子是个例变化的一种常见手段。我们在父类中容易变化的地方放置钩子,钩子可以有一个默认的实现。究竟要不要实现"挂钩",由子类自行决定。

let Breverage = function(){}
        Breverage.prototype.boilWater = function(){
            console.log("把水煮沸")
        }
        
        Breverage.prototype.brew = function(){
            throw new Error("子类必须重写pourInbrew方法")
        }
        Breverage.prototype.porInCup = function(){
            throw new Error("子类必须重写porInCup方法")
        }

        Breverage.prototype.addCondiments = function(){
            throw new Error("子类必须重写addCondiments方法")
        }
        Breverage.prototype.addCondiments = function(){
            throw new Error("子类必须重写addCondiments方法")
        }

        Breverage.prototype.customerWantsCondiments = function(){ //是否需要调料
            throw new Error("子类必须重写addCondiments方法")
        }
        Breverage.prototype.init = function(){
            this.boilWater()
            this.brew()
            this.porInCup()
            if(this.customerWantsCondiments()){
                this.addCondiments()
            }
        }
        let CoffeeWithHook = function(){}

        CoffeeWithHook.prototype = new Breverage()
        CoffeeWithHook.prototype.brew = function(){
            console.log('用沸水冲泡咖啡')
        }
        let coffeeWithHook = new CoffeeWithHook()
        coffeeWithHook.init()

//也可不通过继承实现

        let Breverage = function(param){
            let boilWater = function(){
                console.log("把水煮沸")
            }
            let brew = param.brew || function(){
                throw new Error("子类必须重写pourInbrew方法")
            }
            let porInCup = param.porInCup || function(){
                throw new Error("子类必须重写porInCup方法")
            }

            let addCondiments = param.addCondiments || function(){
                throw new Error("子类必须重写addCondiments方法")
            }
            let customerWantsCondiments = param.customerWantsCondiments || function(){ //是否需要调料
                throw new Error("子类必须重写addCondiments方法")
            }
            let F = function(){}
            F.prototype.init = function(){
                boilWater()
                brew()
                addCondiments()
                customerWantsCondiments()
            }
            return F
        }
        
        let Coffee = Breverage({
            brew:function(){
                console.log('用沸水冲咖啡')
            },
            porInCup:function(){
                console.log('把咖啡倒进杯子')
            }
        })
        let coffee = new Coffee()
        coffee.init()
        let Tea = Breverage({
            brew:function(){
                console.log('用沸水冲茶叶')
            },
            porInCup:function(){
                console.log('把茶倒进杯子')
            }
        })
        let tea = new Tea()
        coffteae.init()

四、职责链模式

·定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,知道有一个对象处理它为止。

应用:①

        let Chain = function(fn){
            this.fn = fn
            this.successor = null
        }
        Chain.prototype.setNextSuccessor = function(successor){
            this.successor = successor
        }
        Chain.prototype.passRequest = function(){ //接受传入的请求
            let ret = this.fn.apply(this,arguments)
            if(ret === 'nextSuccessor'){
                return this.successor && this.successor.passRequest.apply(this.successor,arguments)
            }
            return ret
        }
        let order500 = function(orderType,pay,stock){
            if(orderType ===1 && pay === true){
                console.log('500定金预购,享100优惠卷')
            }else{
                return 'nextSuccessor'
            }
        }
        let order200 = function(orderType,pay,stock){
            if(orderType ===2 && pay === true){
                console.log('200定金预购,享100优惠卷')
            }else{
                return 'nextSuccessor'
            }
        }
        let orderNormal = function(orderType,pay,stock){
            if(stock>0 ){
                console.log('普通购买,无优惠卷')
            }else{
                console.log('手机库存不足')
            }
        }
        //将三个订单函数包装成职责链的节点
        let chainOrder500 = new Chain( order500 )
        let chainOrder200 = new Chain( order200 )
        let chainOrderNormal = new Chain( orderNormal )
        //指定节点再职责链中的顺序
        chainOrder500.setNextSuccessor(chainOrder200)
        chainOrder200.setNextSuccessor(chainOrderNormal)
        //将请求传递给第一个节点
        chainOrder500.passRequest(2,true,0)
        //灵活添加、移除和修改链中的节点顺序,如下
        let order300 = function(){
            //具体实现
        }
        let chainOrder300 = new Chain(order300)
        chainOrder500.setNextSuccessor(chainOrder300)
        chainOrder300.setNextSuccessor(chainOrder200)

②异步的职责链

有事需要根据异步请求返回结果才能决定是否继续在职责链中passRequest(接收传入的参数)

所以需要给china添加一个原型方法China.prototype.next()

China.prototype.next = function(){
            return this.successor && this.successor.passRequest.apply(this.successor,arguments)
        }

③用AOP实现职责链

        Function.prototype.after = function(fn){
            let self = this
            return function(){
                let ret = self.apply(this,arguments)
                if(ret === 'nextSuccessor'){
                    return fn.apply(this,arguments)
                }
                return ret
            }
        }
        let order = order500.after(order200).after(orderNormal)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值