持续创作,加速成长!这是我参与「掘金日新计划 · 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()
}
参考资料