系列目录:javascript
装饰者模式是在不改变对象自身的基础上,在程序运行期间给对象动态地添加方法。java
本章节的示例须要 babel 支持修饰器模式ajax
装饰者模式很是贴合 JavaScript 动态语言的特性,由于咱们能够轻易的改变某个对象,但同时,由于函数是一等公民
,因此咱们会避免直接改写某个函数,来保护代码的可维护性和可扩展性。typescript
其实就像咱们拍照后添加的滤镜
,不一样的滤镜给照片赋予了不一样的意境,这就是装饰者模式,经过滤镜装饰了照片,而并无修改照片自己就给其添加了功能。编程
下面来举一个例子:设计模式
加入此时你是觉得勇者 但勇者当前只有初始数值服务器
class Warrior {
constructor(atk=50, def=50, hp=100, mp=100) {
this.init(atk,def,hp,mp)
}
init(atk, def, hp, mp) {
this.atk = atk
this.def = def
this.hp = hp
this.mp = mp
}
toString() {
return `攻击力:${this.atk}, 防护力: ${this.def}, 血量: ${this.hp}, 法力值: ${this.mp}`
}
}
const Reaper = new Warrior()
console.log(`勇者状态 => ${Reaper}`) // 勇者状态 => 攻击力:50, 防护力: 50, 血量: 100, 法力值: 100
复制代码
以后咱们为勇者配备一把圣剑,建立一个 decorateSword
方法,注意这个方法是装饰在init
上的babel
// 本质是使用了 Object.defineProperty 方法
function decorateSword(target, key, descriptor) {
// 首先获取到 init 方法
const initMethod = descriptor.value
// 宝剑添加攻击力 100 点
let moreAtk = 100
let returnObj
descriptor.value = (...args) => {
args[0] += moreAtk
returnObj = initMethod.apply(target, args)
return returnObj
}
}
class Warrior {
constructor(atk=50, def=50, hp=100, mp=100) {
this.init(atk,def,hp,mp)
}
@decorateSword
init(atk, def, hp, mp) {
this.atk = atk
this.def = def
this.hp = hp
this.mp = mp
}
toString() {
return `攻击力:${this.atk}, 防护力: ${this.def}, 血量: ${this.hp}, 法力值: ${this.mp}`
}
}
const Reaper = new Warrior()
console.log(`勇者状态 => ${Reaper}`) // 勇者状态 => 攻击力:150, 防护力: 50, 血量: 100, 法力值: 100
复制代码
如今,咱们这位勇者的防护力还过低了,咱们须要为勇者再增添一个护甲app
// 省略decorateSword
function decorateArmour(target, key, descriptor) {
// 首先获取到 init 方法
const initMethod = descriptor.value
// 护甲添加防护力 100 点
let moreDef = 100
let returnObj
descriptor.value = (...args) => {
args[1] += moreDef
returnObj = initMethod.apply(target, args)
return returnObj
}
}
class Warrior {
constructor(atk=50, def=50, hp=100, mp=100) {
this.init(atk,def,hp,mp)
}
@decorateSword
@decorateArmour
init(atk, def, hp, mp) {
this.atk = atk
this.def = def
this.hp = hp
this.mp = mp
}
toString() {
return `攻击力:${this.atk}, 防护力: ${this.def}, 血量: ${this.hp}, 法力值: ${this.mp}`
}
}
const Reaper = new Warrior()
console.log(`勇者状态 => ${Reaper}`) // 勇者状态 => 攻击力:150, 防护力: 150, 血量: 100, 法力值: 100
复制代码
咱们成功的让勇者升级了,他终于能够战胜大魔王了(没准仍是个史莱姆)函数
装饰者通常也用来实现 AOP (面向切面编程)利用AOP能够对业务逻辑的各个部分进行隔离,也能够隔离业务无关的功能好比日志上报、异常处理等,从而使得业务逻辑各部分之间的耦合度下降,提升业务无关的功能的复用性,也就提升了开发的效率。
装饰者模式与代理模式相似,但代理模式的意图是不直接访问实体,为实体提供一个替代者,实体内定义了关键功能,而代理提供或拒绝对实体的访问,或者一些其余额外的事情。而装饰者模式的做用就是为对象动态地加入行为。
外观模式为一组复杂的子系统接口提供一个更高级的统一接口,经过这个接口使得对子系统接口的访问更容易,不符合单一职责原则和开放封闭原则。
其实外观模式很常见,它其实就是经过一个单独的函数,来简化对一个或多个更大型,更为复杂的函数的访问,是一种对复杂操做的封装。
初始化一个原生 Ajax 请求实际上是复杂的,咱们能够经过封装来简化
function ajaxCall(type, url, callback, data) {
let xhr = (function(){
try {
// 标准方法
return new XMLHttpRequest()
}catch(e){}
try {
return new ActiveXObject("Msxm12.XMLHTTP")
}catch(e){}
}())
STATE_LOADED = 4
STATUS_OK = 200
// 一但从服务器收到表示成功的相应消息,则执行所给定的回调方法
xhr.onreadystatechange = function () {
if (xhr.readyState !== STATE_LOADED) {
return
}
if (xhr.state == STATUS_OK) {
callback(xhr.responseText)
}
}
// 发出请求
xhr.open(type.toUpperCase(), url)
xhr.send(data)
}
复制代码
封装以后,咱们发送请求的样子就变成了
// 使用封装的方法
ajaxCall("get", "/url/data", function(res) {
document.write(res)
})
复制代码
外观模式适用于当须要同时有多个复杂操做时,经过把复杂操做封装,调用时直接用方法调用,提升代码的可阅读性和可维护性。
中介者模式的主要做用是解除对象之间的强耦合关系,经过增长一个中介者,让全部的对象经过中介者通讯,而不是相互引用,因此当一个对象发生改变时,只须要通知中介者对象便可。
中介者但是使网状的多对多关系变为相对简单的一对多关系。
假设此时有两队人在玩 英雄联盟 ,必须团灭对方全部玩家才能得到胜利。下面将分为蓝红方:
class Player {
constructor(name, teamColor) {
this.name = name // 英雄名称
this.teamColor = teamColor // 队伍颜色
this.teammates = [] // 队友列表
this.enemies = [] // 敌人列表
this.state = 'alive' // 存活状态
}
// 获胜
win() {
console.log(`Vicotry! ${this.name}`)
}
// 失败
lose() {
console.log(`Defeat! ${this.name}`)
}
// 死亡方法
die() {
// 团灭标志
let ace_flag = true
// 设置玩家状态为死亡
this.state = 'dead'
// 遍历队友列表,若没有团灭,则还未失败
for(let i in this.teammates) {
if (this.teammates[i].state !== 'dead') {
ace_flag = false
break
}
}
// 若是已被团灭
if (ace_flag === true) {
// 己方失败
this.lose()
for(let i in this.teammates) {
this.teammates[i].lose()
}
// 敌方胜利
for(let i in this.enemies) {
this.enemies[i].win()
}
}
}
}
// 玩家列表
const Players = []
// 定义一个工厂函数来生成玩家
function playerFactory (name, teamColor) {
let newPlayer = new Player(name, teamColor)
// 通知全部玩家 新角色加入
for(let i in Players) {
if (Players[i].teamColor === teamColor) {
Players[i].teammates.push(newPlayer)
newPlayer.teammates.push(Players[i])
} else {
Players[i].enemies.push(newPlayer)
newPlayer.enemies.push(Players[i])
}
}
Players.push(newPlayer)
return newPlayer
}
// 开始比赛
// 蓝色方
let hero1 = playerFactory('盖伦', 'Blue')
let hero2 = playerFactory('皇子', 'Blue')
let hero3 = playerFactory('拉克丝', 'Blue')
let hero4 = playerFactory('剑姬', 'Blue')
let hero5 = playerFactory('赵信', 'Blue')
// 红色方
let hero6 = playerFactory('诺手', 'Red')
let hero7 = playerFactory('德莱文', 'Red')
let hero8 = playerFactory('卡特琳娜', 'Red')
let hero9 = playerFactory('乌鸦', 'Red')
let hero10 = playerFactory('赛恩', 'Red')
// 红色方被团灭
hero6.die()
hero7.die()
hero8.die()
hero9.die()
hero10.die()
/* 运行结果: Defeat! 赛恩 Defeat! 诺手 Defeat! 德莱文 Defeat! 卡特琳娜 Defeat! 乌鸦 Vicotry! 盖伦 Vicotry! 皇子 Vicotry! 拉克丝 Vicotry! 剑姬 Vicotry! 赵信 */
复制代码
但这只是一局比赛的状况,假若咱们此时存在掉线或者更换队伍的状况,那么上面的这种形式是晚觉没法解决的,因此此时咱们须要一个中介者来统管全部的玩家。
const GameManager = ( function() {
// 存储全部玩家
const players = []
// 操做实体
const operations = {}
// 新增玩家
operations.addPlayer = function (player) {
let teamColor = player.teamColor
players[teamColor] = players[teamColor] || []; // 若是该颜色的玩家尚未成立队伍,则新成立一个队伍
players[teamColor].push(player); // 添加玩家进队伍
}
// 玩家掉线
operations.playerDisconnect = function (player) {
// 玩家队伍颜色
let teamColor = player.teamColor
let teamPlayer = players[teamColor]
for(let i in teamPlayer) {
if (teamPlayer[i].name = player.name) {
teamPlayer.splice(i, 1)
}
}
}
// 玩家死亡
operations.playerDead = function (player) {
let teamColor = player.teamColor
teamPlayers = players[teamColor]
// 团灭标志
let ace_flag = true
// 设置玩家状态为死亡
this.state = 'dead'
// 遍历队友列表,若没有团灭,则还未失败
for(let i in teamPlayers) {
if (teamPlayers[i].state !== 'dead') {
ace_flag = false
break
}
}
// 若是已被团灭
if (ace_flag === true) {
// 己方失败
for(let i in teamPlayers) {
teamPlayers[i].lose()
}
// 敌方胜利
for(let color in players) {
if (color !== teamColor) {
let teamPlayers = players[color]
teamPlayers.map(player => {
player.win()
})
}
}
}
}
function reciveMessage (message, player) {
operations[message](player)
}
return {
reciveMessage: reciveMessage
}
})()
class Player {
constructor(name, teamColor) {
this.name = name // 英雄名称
this.teamColor = teamColor // 队伍颜色
this.state = 'alive' // 存活状态
}
// 获胜
win() {
console.log(`Vicotry! ${this.name}`)
}
// 失败
lose() {
console.log(`Defeat! ${this.name}`)
}
// 死亡方法
die() {
// 设置玩家状态为死亡
this.state = 'dead'
// 向中介者发送死亡的宣告
GameManager.reciveMessage('playerDead', this)
}
// 玩家掉线
disconnect() {
GameManager.reciveMessage('playerDisconnect', this)
}
}
// 玩家列表
const Players = []
// 定义一个工厂函数来生成玩家
function playerFactory (name, teamColor) {
let newPlayer = new Player(name, teamColor)
// 通知中介者新增玩家
GameManager.reciveMessage('addPlayer', newPlayer)
return newPlayer
}
// 开始比赛
// 蓝色方
let hero1 = playerFactory('盖伦', 'Blue')
let hero2 = playerFactory('皇子', 'Blue')
let hero3 = playerFactory('拉克丝', 'Blue')
let hero4 = playerFactory('剑姬', 'Blue')
let hero5 = playerFactory('赵信', 'Blue')
// 红色方
let hero6 = playerFactory('诺手', 'Red')
let hero7 = playerFactory('德莱文', 'Red')
let hero8 = playerFactory('卡特琳娜', 'Red')
let hero9 = playerFactory('乌鸦', 'Red')
let hero10 = playerFactory('赛恩', 'Red')
// 红色方被团灭
hero6.die()
hero7.die()
hero8.die()
hero9.die()
hero10.die()
/* 运行结果: Defeat! 赛恩 Defeat! 诺手 Defeat! 德莱文 Defeat! 卡特琳娜 Defeat! 乌鸦 Vicotry! 盖伦 Vicotry! 皇子 Vicotry! 拉克丝 Vicotry! 剑姬 Vicotry! 赵信 */
复制代码
中介者模式就是用来下降耦合度的,全部若是你的代码或者模块中耦合度较高,依赖过分,对实际调用和维护产生了影响,那么就能够经过中介者模式来下降耦合度。