持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情
关键词:结构型模式 Decorator Wrapper 动态增加功能 原型高达挂载武器模块
在设计上更像是 AOP 的设计思路。也可以理解成 react 中的 HOC。
解决什么问题?
装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。
它主要的作用是给原始类添加增强功能。用于一开始不能确定对象的全部功能时,为对象动态加入行为,也可以说是增强功能。
相对于继承,有两个特殊的地方:
- 装饰器和原始类继承同样的父类,这样我们可以对原始类“嵌套”多个装饰器类。
- 装饰器是对功能的增强,这也是装饰器模式应用场景的一个重要特点。
举个例子:
继承的加强类似假两件,装饰器的加强类似单品穿搭。
class Body { wear() { console.log('Body!') } } class TshirtDecorator { constructor(body) { this.body = body } wear() { this.body.wear(); console.log('穿T恤') } } class JacketDecorator { constructor(body) { this.body = body } wear() { this.body.wear() console.log('穿夹克') } } let p = new Body(); p = new TshirtDecorator(p) p = new JacketDecorator(p) p.wear() // Body! // 穿T恤 // 穿夹克
应用场景
不希望某个类天生就非常庞大,一次性包含许多职责。那么我们就可以使用装饰者模式。
侧重于功能,或者拆分方法的时候,推荐使用装饰器模式。并且可以嵌套多个装饰器类。
具体实践
原始代码
修改为一个简单的 HOC 组件
外面再嵌套一个装饰器
工具函数一览
const before = (fn, beforefn) => { return function () { beforefn.apply(this, arguments) return fn.apply(this,arguments) } } const after = (fn, afterfn) => { return function () { const res = fn.apply(this,arguments) afterfn.apply(this, arguments) return res } } let bf = before(function () { alert(3) }, function () { alert(2) }) bf = before(bf, function () { alert(1) }) bf() let af = after(function () { alert(3) }, function () { alert(2) }) af = after(af, function () { alert(1) }) af()
数据统计上报
// html <button id="btn" tag="login">点击打开登录框</button> const log = (tag) => { console.log(`上报标签为${tag}`) } function showLogin() { console.log('打开弹窗', this) log(this.getAttribute('tag')) } // 页面登录按钮,点击弹出登录框,并进行数据上报 document.getElementById('btn').addEventListener('click', showLogin)
解耦数据上报和登录弹窗两项功能。
const after = (fn, afterfn) => { return function () { const res = fn.apply(this, arguments) afterfn.apply(this, arguments) return res } } function showLogin() { console.log('打开弹窗') } function log() { console.log(`上报标签为${this.getAttribute('tag')}`) } showLogin = after(showLogin, log) document.getElementById('btn').addEventListener('click', showLogin)
动态修改函数参数
const before = (fn, beforefn) => { return function () { beforefn.apply(this, arguments) return fn.apply(this, arguments) } } const getToken = () => 'token' function ajax(type, url, params) { console.table(params) } // 给请求添加token ajax = before(ajax, function (type, url, params) { params.token = getToken() }) ajax('get', 'url', { name: 'kane' })
插件式表单验证
// html // 用户名 <input type="text" id="name"/> // 密码 <input type="password" id="pwd"/> // <button id="btn" tag="login">提交</button> const name = document.getElementById('name'); const pwd = document.getElementById('pwd'); const btn = document.getElementById('btn'); Function.prototype.before = function (beforefn) { const _this = this; return function () { if (beforefn.apply(this, arguments) === false) return; return _this.apply(this, arguments) } } const validata = () => { if (name.value === '' || pwd.value === '') { alert(`不能为空`) return false } return true } let submit = () => { const params = { name: name.value, pwd: pwd.value, } // 提交数据 console.log(params) } submit = submit.before(validata) btn.onclick = () => { submit() }
参考资料