装饰器实现过程描述->如何使用装饰器写出优雅的代码

装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

何时使用:在不想增加很多子类的情况下扩展类。

如何解决:将具体功能职责划分,同时继承装饰者模式。

一、装饰器形成过程

下面通过一个例子来实现装饰器形成过程:

1 函数直接调用,无任何修饰

class Log {
//普通函数 
    print(msg){
        console.log(msg)
    }
}

dec(Log,'print')
// 1 直接调用,无任何修饰
const log = new Log()
log.print('hello kicy')

2、装饰器: 普通函数调用,对Log进行了修饰
以上例子,对print()方法结果进行重组,想在这里执行的结果再传一个参数,本身的函数不能再传参了,解决办法—>升阶
引出一个概念:装饰器工厂
原来的是一个“装饰器函数“,变成了制造 ” 装饰器函数“ 的 ”函数“,所以它叫装饰器工厂
实现代码:

class Log {
    print(msg){
        console.log(msg)
    }
}
// 装饰器工厂
const dec = (target,property) => {
    const old = target.prototype.print
    target.prototype[property] = msg => {
        console.log('执行了' + property)
        msg = `{${msg}}`
        old(msg)
    }
}
//修饰print
dec(Log,'print')

const log = new Log()
log.print('hello kicy')

**3 装饰器工厂(想有更多的定制,如:在dec函数上再传个参数,进一步修饰–>使用升阶方式–即高阶函数(因为装饰器本身没有再传参机会,所以要使用升阶的方式) **

const decUp = (name) => (target,property) => {
    const old = target.prototype[property] //把原来属性拿出来,再写个新的
    target.prototype[property] = msg => {
        console.log('执行了:'+property)
        msg = `进一步装饰: '${msg}: ${name}'`
        old(msg)
    }
}
//传递多个参数
decUp('kicy')(Log,'print')


//实例化
const log = new Log()
//调用执行
log.print('hello')

4 注解型装饰器
3这种写法不优雅,选择装饰器的写法 注解类型的装饰器(叫装饰器或者注解都可以)
@是装饰器的语法糖
实现原理:返回的是属性描述符,其实用的是definproperty

class Log2 {
    @decorate
    print(msg){
        console.log(msg)
    }
}

function decorate(target,property,descriptor){
    const old = descriptor.value //descriptor.value从属性描述符种取出value就是属性本身, 获取方法本身,这里对应的是print()
    descriptor.value = msg => {
        msg = `装饰器: [${msg}]`
        return old.apply(null,[msg])
    }
    return descriptor
}
const log2 = new Log2()
//调用执行
log2.print('hello2')

注解型装饰器实际的实现原理:它是通过descriptor属性描述符,最终返回decriptor这个描述符,其实就是使用defineProperty,再设置到对应的属性上就可以了
装饰器仿写,这个方法的最后一层封印 ,仿造的是上面@decorate的写法,装饰器内部实现的原理就是使用的defineProperty

传入一个修饰器decorate,方法内部对修饰器decorate进行一个实现,再用 definProperty 设置进去,这是对原来装饰器语法糖的简单模拟

const anotation = (target,property,decorate) => {
    const decorator = decorate(target.prototype,property,Object.getOwnPropertyDescriptor(target.prototype,property))
    Object.defineProperty(target.prototype,property,decorator)
}
anotation(Log,'print',decorate)

二、实战例子

定义 decto.ts

import * as glob from 'glob' //读取文件夹的文件,可以迭代
import * as Koa from 'koa'
import * as KoaRouter from 'koa-router'

const router = new KoaRouter()

//1 get和post重复性高,再升阶进行复用
// export const get = path => (target,property) => {
//     router['get'](path,target[property])
// }
// export const post = path => (target,property) => {
//     router['post'](path,target[property])
// }


//2 传入method,升阶
// export const method = method => (path:string,options?) => {
//     return (target,property) => {
//         const url = options && options.prefix ? options.prefix + path : path
//        //router 在这里使用作用域传入,不符合传输透明原则,改为参数传进来
//         router[method](path,target[property])
//     }
// }

// export const get = method('get')
// export const post = method('post')

//3 传入router,再升阶
export const method = router => method => (path:string,options?) => {
    return (target,property) => {
        //注意:不同的中间件根据需求有不同的执行顺序(如token的需要先执行,因为要先获取daotoken,再往下执行其他的),通过nextTick方式调整
        process.nextTick(()=>{
            //注册中间件
            const middlewares = []

            console.log('target.middlewares',target.middlewares)

            //获取class类中的中间件
            if(target.middlewares){
                middlewares.push(...target.middlewares)
            }

            if(options && options.middlewares){
                middlewares.push(...options.middlewares)
            }
            middlewares.push(target[property])

            const url = options && options.prefix ? options.prefix + path : path
        //router 在这里使用作用域传入,不符合传输透明原则,改为参数传进来
            // router[method](url,target[property])
            router[method](url,...middlewares)
        })
        

    }
}

const decorate = method(router)
export const get = decorate('get')
export const post = decorate('post')

//中间件
export function middlewares(middlewares){
    return function(target){
        target.prototype.middlewares = middlewares
    }
}

//load就是读入文件(这里是routes)列表
export const load = (folder:string) : KoaRouter => {
    const extname = '.{js,ts}'
    //glob 扫描所有的文件夹,相当于递归
    //glob.sync同步读取, ./**/*${extname} 表示可以加载对应文件夹下无限层级的文件夹下的js/ts文件
    glob.sync(require('path')
        .join(folder,`./**/*${extname}`))
        .forEach(item => require(item)); //依次加载,加载所有的class,它们会执行对应的装饰器语法,而装饰器语法会把多有的router里面的规则进行设置
    return router
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值