【前端谈设计】装饰器模式

222 阅读3分钟

前言

装饰器模式在前端开发中属于那种听得多但用的少的结构型设计模式,听得多,是因为它很容易被开发者理解,就算不知道它的人也能通过名称说出点大概内容和含义,之所以又说它用的少,其实这里也只是说它在前端开发使用的很少,而它在后端开发中绝对是常客。

前端之所以使用的少,我认为可能是因为JS语言的特性是动态改变,而装饰器模式更适合场景的是静态不变,本身两者的设计理念就有偏差。但是话又说回来,存在即有意义,不能去否定装饰器模式的观念,毕竟在TS的装饰器语法中,还是给装饰器模式留了一亩三分地的。

装饰器模式

为目标添加新功能,同时还不影响目标其原有的结构和功能

就比如给手机配手机壳,既给手机添加了防摔的功能,还不会影响到手机的原有的功能并且手机还可以有其他的装饰,比如手机膜、手机支架等等。如果哪天你不需要手机壳,也可以安心的把它给去掉,因为去掉它也并不会影响手机的功能,手机和手机的所有装饰都是独立存在的,互相成就但并不互相依赖。

装饰器和目标隔离,通过新增装饰器给目标添加新功能,完美的体现了开放封闭原则单一职责原则

image-20230224211906683

JS的装饰器

装饰器按照作用目标进行分类,可以分为类装饰器方法装饰器属性装饰器访问器装饰器参数装饰器对象装饰器函数装饰器等。还是那句老话,只要遵循装饰器的不影响原则,那么它就属于该目标的装饰器。

对象装饰器

回到上面手机的例子,现在给手机添加手机壳。试试如何通过JS语法去实现吧?

let phone = {
    // 打电话
    call() {},
    // 安装手机壳
    shell() {
        console.log("手机壳");
    }
}

给`Phone类对象内部添加属性嘛?No!No!No!!!

这虽然是我们能最快想到的方法,但是这个方法显然不符合不破坏手机原有功能和结构的前提条件

let phone = {
    // 打电话
    call() {},
}

function shellDecorator(phone) {
    // 安装手机壳
    phone.shell = function () {
        console.log("手机壳");
    }
}
phone = shellDecorator(phone)

通过shellDecorator方法不仅可以添加新的功能,还不会影响phone原有结构和功能,shellDecorator方法也就被称为装饰器

其实通过这个例子也能猜出,为什么装饰器模式在JS中使用的少的原因,在JS中,修改对象最常用的方式就是动态改变对象属性和方法,而不是通过装饰器去曲线救国

函数装饰器

场景:你做的电影订票APP已经上线一个月了,通过产品经理说,运维那边需要用户喜好数据,使用在程序中需要知道用户点击每种电影次数。

电影点击的入口很多,如果每个功能模块都去修改,这显然是很不明智的

Function.prototype.after = function (func) {
    let _this = this
    return function () {
        func.apply(this, arguments)
        _this.apply(this, arguments)
    }
}

function movieDetail() {
    console.log(`${name}电影详情`);
}
movieDetail = movieDetail.after(function (name) {
    console.log(`${name}电影点击次数+1`)
})

movieDetail("前任3")

在不修改movieDetail函数的结构和内容的前提下,修改movieDetail函数的功能。函数装饰器的使用场景还是很多的,比如函数报错监听、函数执行日志等

后言

只要符合装饰器模式不影响原目标的原则,其实都能称为装饰器,装饰器在TS被广泛使用,在JS中却很少有人提及

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 24 天,点击查看活动详情