Series catalog:
- [1] — Creative design Patterns
- JavaScript Design Pattern Parsing [2] — Structural design Pattern
- JavaScript Design Pattern Parsing [3] — Behavioral design Patterns
Decorator pattern
Decorator mode is to dynamically add methods to objects while the program is running without changing the objects themselves.
The examples in this section require Babel to support the decorator pattern
The decorator pattern fits well with the dynamic nature of JavaScript because we can easily change an object, but at the same time, because functions are first-class citizens, we avoid rewriting a function directly to protect the maintainability and extensibility of our code.
In fact, it’s like the filter we add after taking a photo. Different filters give different mood to the photo. This is the decorator mode, which decorates the photo through the filter without modifying the photo itself.
Here’s an example:
The initial instance
You are considered brave at this point but the brave currently only has the initial number
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 'Attack power:The ${this.atk}, defense:The ${this.def}HP:The ${this.hp}, mana value:The ${this.mp}`}}const Reaper = new Warrior()
console.log('Brave state =>${Reaper}`) // Brave Status => Damage :50, Defense :50, Health: 100, Mana: 100
Copy the code
The use of decorators
We then equip the brave with a sword and create a decorateSword method, which is decorated on init
// The Object. DefineProperty method is used
function decorateSword(target, key, descriptor) {
// Get init first
const initMethod = descriptor.value
// Sword adds 100 damage
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 'Attack power:The ${this.atk}, defense:The ${this.def}HP:The ${this.hp}, mana value:The ${this.mp}`}}const Reaper = new Warrior()
console.log('Brave state =>${Reaper}`) // Brave Status => Damage :150, Defense: 50, Health: 100, Mana: 100
Copy the code
The stack of decorators
Right now, our hero’s defense is too low, we need to add another armor to the hero
/ / omit decorateSword
function decorateArmour(target, key, descriptor) {
// Get init first
const initMethod = descriptor.value
// Armor adds 100 defense
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 'Attack power:${this.atk}, defense:${this.def}HP:${this.hp}, mana value:${this.mp}`}}const Reaper = new Warrior()
console.log('Brave state =>${Reaper}`) // Warrior Status => Damage :150, Defense :150, Health: 100, Mana: 100
Copy the code
We have successfully upgraded the brave, and he can finally defeat the demon Lord (or a Slime).
Conclusion:
Decorators are also typically used to implement AOP (aspect oriented programming) using AOP the various parts of the business logic can be isolated, can also be isolated unrelated business report functions such as logging, exception handling, etc., so as to make the business logic between the parts of the coupling is reduced, improve the reusability of business irrelevant features, also improve the efficiency of development.
The decorator pattern is similar to the proxy pattern, but the intention of the proxy pattern is not to directly access the entity, to provide a substitute for the entity, to define key functions within the entity, and to provide or deny access to the entity, or some other additional thing. The purpose of decorator pattern is to dynamically add behavior to an object.
The appearance model
The facade pattern provides a higher level unified interface for a complex set of subsystem interfaces, through which access to subsystem interfaces is easier, and does not conform to the single responsibility principle and open closed principle.
In fact, the facade pattern is very common, it is a single function to simplify access to one or more larger, more complex functions, is a kind of encapsulation of complex operations.
Encapsulation Ajax
Initializing a native Ajax request is complex and can be simplified by encapsulation
function ajaxCall(type, url, callback, data) {
let xhr = (function(){
try {
// Standard method
return new XMLHttpRequest()
}catch(e){}
try {
return new ActiveXObject("Msxm12.XMLHTTP")}catch(e){}
}())
STATE_LOADED = 4
STATUS_OK = 200
// As soon as a corresponding message indicating success is received from the server, the given callback method is executed
xhr.onreadystatechange = function () {
if(xhr.readyState ! == STATE_LOADED) {return
}
if (xhr.state == STATUS_OK) {
callback(xhr.responseText)
}
}
// Make a request
xhr.open(type.toUpperCase(), url)
xhr.send(data)
}
Copy the code
After encapsulation, we send the request like this
// Use the encapsulation method
ajaxCall("get"."/url/data".function(res) {
document.write(res)
})
Copy the code
conclusion
The appearance mode is suitable for multiple complex operations at the same time. By encapsulating complex operations and calling them directly with methods, the code can be improved in terms of readability and maintainability.
The mediator pattern
The main role of the mediator pattern is to remove the strong coupling between objects. By adding a mediator, all objects communicate through the mediator instead of referring to each other, so when an object changes, only the mediator objects need to be notified.
The mediator transforms the netted many-to-many relationship into a relatively simple one-to-many relationship.
Example scenario
Let’s say two teams are playing league of Legends, and you have to kill all the other players to win. The following will be divided into blue and red:
class Player {
constructor(name, teamColor) {
this.name = name // Hero name
this.teamColor = teamColor // Team color
this.teammates = [] // List of teammates
this.enemies = [] // List of enemies
this.state = 'alive' // Live state
}
/ / win
win() {
console.log(`Vicotry! The ${this.name}`)}/ / fail
lose() {
console.log(`Defeat! The ${this.name}`)}// Method of death
die() {
// Block out flag
let ace_flag = true
// Set the player state to dead
this.state = 'dead'
// Iterate through the list of teammates
for(let i in this.teammates) {
if (this.teammates[i].state ! = ='dead') {
ace_flag = false
break}}// If it has been destroyed
if (ace_flag === true) {
// Your side failed
this.lose()
for(let i in this.teammates) {
this.teammates[i].lose()
}
// The enemy wins
for(let i in this.enemies) {
this.enemies[i].win()
}
}
}
}
// Player list
const Players = []
// Define a factory function to generate players
function playerFactory (name, teamColor) {
let newPlayer = new Player(name, teamColor)
// Notify all players of the new character
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
}
// Start the match
/ / blue party
let hero1 = playerFactory('galen'.'Blue')
let hero2 = playerFactory('prince'.'Blue')
let hero3 = playerFactory('Lacos'.'Blue')
let hero4 = playerFactory('sword she'.'Blue')
let hero5 = playerFactory('xenzhao'.'Blue')
/ / red square
let hero6 = playerFactory('hand,'.'Red')
let hero7 = playerFactory('Delevin'.'Red')
let hero8 = playerFactory(katarina.'Red')
let hero9 = playerFactory('the raven'.'Red')
let hero10 = playerFactory('after'.'Red')
// The red square is destroyed by the mass
hero6.die()
hero7.die()
hero8.die()
hero9.die()
hero10.die()
/* Result: Defeat! Thain Defeat! Defeat's hand! DE levin Defeat! Catalina Defeat! The crow Vicotry! Galen Vicotry! Prince Vicotry! Lacus Vicotry! Jian ji Vicotry! Xenzhao * /
Copy the code
However, this is only for one game, if we have a drop or team change, then the above situation can not be solved late in the night, so we need a mediator to control all players.
Refactoring using the mediator pattern
- The Player and palyerFactory base operations remain the same
- Assign the operation corner to the mediator object
const GameManager = ( function() {
// Store all players
const players = []
// Operate entities
const operations = {}
// Add new players
operations.addPlayer = function (player) {
let teamColor = player.teamColor
players[teamColor] = players[teamColor] || []; // If the player of this color does not already have a team, create a new team
players[teamColor].push(player); // Add players to the team
}
// The player dropped the call
operations.playerDisconnect = function (player) {
// Player team color
let teamColor = player.teamColor
let teamPlayer = players[teamColor]
for(let i in teamPlayer) {
if (teamPlayer[i].name = player.name) {
teamPlayer.splice(i, 1)}}}// The player dies
operations.playerDead = function (player) {
let teamColor = player.teamColor
teamPlayers = players[teamColor]
// Block out flag
let ace_flag = true
// Set the player state to dead
this.state = 'dead'
// Iterate through the list of teammates
for(let i in teamPlayers) {
if(teamPlayers[i].state ! = ='dead') {
ace_flag = false
break}}// If it has been destroyed
if (ace_flag === true) {
// Your side failed
for(let i in teamPlayers) {
teamPlayers[i].lose()
}
// The enemy wins
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 // Hero name
this.teamColor = teamColor // Team color
this.state = 'alive' // Live state
}
/ / win
win() {
console.log(`Vicotry! The ${this.name}`)}/ / fail
lose() {
console.log(`Defeat! The ${this.name}`)}// Method of death
die() {
// Set the player state to dead
this.state = 'dead'
// Send a declaration of death to the intermediary
GameManager.reciveMessage('playerDead'.this)}// The player dropped the call
disconnect() {
GameManager.reciveMessage('playerDisconnect'.this)}}// Player list
const Players = []
// Define a factory function to generate players
function playerFactory (name, teamColor) {
let newPlayer = new Player(name, teamColor)
// Notify the broker of new players
GameManager.reciveMessage('addPlayer', newPlayer)
return newPlayer
}
// Start the match
/ / blue party
let hero1 = playerFactory('galen'.'Blue')
let hero2 = playerFactory('prince'.'Blue')
let hero3 = playerFactory('Lacos'.'Blue')
let hero4 = playerFactory('sword she'.'Blue')
let hero5 = playerFactory('xenzhao'.'Blue')
/ / red square
let hero6 = playerFactory('hand,'.'Red')
let hero7 = playerFactory('Delevin'.'Red')
let hero8 = playerFactory(katarina.'Red')
let hero9 = playerFactory('the raven'.'Red')
let hero10 = playerFactory('after'.'Red')
// The red square is destroyed by the mass
hero6.die()
hero7.die()
hero8.die()
hero9.die()
hero10.die()
/* Result: Defeat! Thain Defeat! Defeat's hand! DE levin Defeat! Catalina Defeat! The crow Vicotry! Galen Vicotry! Prince Vicotry! Lacus Vicotry! Jian ji Vicotry! Xenzhao * /
Copy the code
When to use it?
The mediator mode is used to reduce the coupling, so if your code or module is highly coupled, overly dependent, and affects the actual call and maintenance, then you can use the mediator mode to reduce the coupling.
conclusion
- The mediator pattern conforms to the least knowledge principle
- The mediator pattern reduces the coupling between objects and modules
- The mediator pattern transforms a complex mesh many-to-many model into a relatively simple one-to-many relationship.
- The mediator model also has some disadvantages. For example, the mediator object is relatively complex and large, and sometimes the mediator itself is difficult to maintain.