模板方法模式
定义一个超类,超类中定义一系列方法,定义了一个模板————封装了完成一个功能、行为的步骤流程,子类根据需要重载超类方法,但是会调用超类模板方法,按照模板规定行为运行
案例1
/*
*定义一个游戏运行模板,负责运行各种游戏
*/
class Game {
register() {
console.log('角色注册')
}
initialize() {
console.log('初始化游戏')
}
start() {
console.log('开始玩游戏')
}
end() {
console.log('结束游戏')
}
//模板方法-抽象出不变的流程步骤,每步具体操作可变
play() {
this.register()
this.initialize()
this.start()
this.end()
}
}
class Card extends Game {
constructor(name) {
super(name)
this.name = name
}
register() {
console.log(`${this.name} - 注册成功`)
}
initialize() {
console.log(`${this.name} - 游戏开始初始化`)
}
start() {
console.log(`${this.name} - 游戏已经开始`)
}
end() {
console.log(`${this.name} - 游戏已经结束`)
}
}
class Ball extends Game {
constructor(name) {
super(name)
this.name = name
}
register() {
console.log(`${this.name} - 注册成功`)
}
initialize() {
console.log(`${this.name} - 足球游戏开始初始化`)
}
start() {
console.log(`${this.name} - 足球游戏已经开始`)
}
end() {
console.log(`${this.name} - 足球游戏已经结束`)
}
}
const cardGame = new Card('卡牌游戏')
cardGame.play()
const ballGame = new Ball('球类游戏')
ballGame.play()
钩子方法
在具体场景应用时,有时需要需要模板方法中封装的流程可以定制化,这时可以使用钩子方法
class Game {
register() {
console.log('角色注册')
}
//执行注册开关
registerSwitch() {
return true
}
initialize() {
console.log('初始化游戏')
}
start() {
console.log('开始玩游戏')
}
end() {
console.log('结束游戏')
}
//模板方法-抽象出不变的流程步骤,每步具体操作可变
play() {
if (this.registerSwitch()) {
this.register()
}
this.initialize()
this.start()
this.end()
}
}
//Football游戏不需要注册就可以玩
class Football extends Game {
constructor(name, type) {
super(name)
this.name = name
this.type = type //游戏是否收费 0:收费 1:免费
}
register() {
console.log(`${this.name} - 注册成功`)
}
registerSwitch() {
let _opt = {
0: () => {
return true
},
1: () => {
return false
}
}
return _opt[this.type]()
}
initialize() {
console.log(`${this.name} - 足球游戏开始初始化`)
}
start() {
console.log(`${this.name} - 足球游戏已经开始`)
}
end() {
console.log(`${this.name} - 足球游戏已经结束`)
}
}
const fb0 = new Football('football0', 0)
const fb1 = new Football('football1', 1)
fb0.play() //需要注册
fb1.play() //不需要注册
享元模式
通过减少对象创建数量,以减少内存占用和提高性能。
具体通过分离出内部状态和外部状态来实现
- 内部状态存储在对象内部,决定创建对象个数,内部状态可以看做对象在内存中的标识符,没有时创建存在时直接返回对象
- 外部状态和应用场景相关,在需要时传入对象中,可以看做具体业务时的需要的外部数据
案例
现在需要绘制1000个图形,其中方形、圆形、椭圆形、三角形各250个,每种类型的颜色、大小均随机
正常实现: 创建了1000个对象去执行绘制操作
const DrawTools = {
//方形
square: function draw(params) {
console.log(`方形 - w:${params.w} - h:${params.h} -
颜色:${params.color}`)
},
//圆形
circle: function draw(params) {
console.log(`圆形 - 半径:${params.radius} -
颜色:${params.color}`)
},
//椭圆形
ellipse: function draw(params) {
console.log(`椭圆形 - a:${params.a} - b:${params.b}-
颜色:${params.color}`)
},
//三角形
triangle: function draw(params) {
console.log(`三角形 - x:${params.x} -
y:${params.y} - z:${params.z} -
颜色:${params.color}`)
}
}
function Graphics(type) {
this.type = type //对象初始化时存储,内部状态
}
Graphics.prototype.draw = function(operator) {
operator[this.type](this.params)
}
Graphics.prototype.setParams = function (params) {
this.params = params //外部状态,具体业务场景时在传入对象的数据
}
function drawDemo(num = 1000) {
let startT = +new Date
let aver = num / 4
let color = ['blue', 'green', 'yellow', 'white']
for (let i = 1; i <= num; i++) {
if (i <= aver) { //画方形
let wh = [[10, 2], [2, 4], [7,8]][Math.floor((Math.random()*3))]
let square = new Graphics('square')
//具体场景数据,不同尺寸、颜色
square.setParams({
w: wh[0],
h: wh[1],
color: color[Math.floor(Math.random()*color.length)]
})
square.draw(DrawTools)
}else if (i <= 2 * aver) { //圆形
let radius = [1, 2, 3, 5, 9]
let circle = new Graphics('circle')
circle.setParams({
radius: radius[Math.floor(Math.random()*radius.length)],
color: color[Math.floor(Math.random()*color.length)]
})
circle.draw(DrawTools)
}else if (i <= 3 * aver) { //椭圆形
let ab = [[1, 2], [3, 4], [6, 8]][Math.floor((Math.random()*3))]
let ellipse = new Graphics('ellipse')
ellipse.setParams({
a: ab[0],
b: ab[1],
color: color[Math.floor(Math.random()*color.length)]
})
ellipse.draw(DrawTools)
}else if(i <= 4 * aver) { //三角形
let xyz = [[6, 7, 8], [3, 4, 5], [2, 1, 2]]
let triangle = new Graphics('triangle')
triangle.setParams({
x: xyz[0],
y: xyz[1],
z: xyz[2],
color: color[Math.floor(Math.random()*color.length)]
})
triangle.draw(DrawTools)
}
}
console.log(`用时 - ${new Date - startT}ms`)
}
drawDemo()
享元模式实现:创建了4个对象完成绘制
- 对象通过工厂函数创建
- 外部状态在业务需要时,通过特定方法传入对象内部
const DrawTools = {
//方形
square: function draw(params) {
console.log(`方形 - w:${params.w} - h:${params.h} -
颜色:${params.color}`)
},
//圆形
circle: function draw(params) {
console.log(`圆形 - 半径:${params.radius} -
颜色:${params.color}`)
},
//椭圆形
ellipse: function draw(params) {
console.log(`椭圆形 - a:${params.a} - b:${params.b}-
颜色:${params.color}`)
},
//三角形
triangle: function draw(params) {
console.log(`三角形 - x:${params.x} -
y:${params.y} - z:${params.z} -
颜色:${params.color}`)
}
}
function Graphics(type) {
this.type = type //内部状态
}
Graphics.prototype.draw = function(operator) {
operator[this.type](this.params)
}
Graphics.prototype.setParams = function (params) {
this.params = params //外部状态
}
//工厂函数,内部状态决定对象个数
var GraphicsFactory = (function () {
let _objCache = {}
return {
create(type) {
return _objCache[type] ? _objCache[type]
: (_objCache[type] = new Graphics(type))
},
getObjCache() {
return _objCache
}
}
})()
function drawDemo(num = 1000) {
let startT = +new Date
let aver = num / 4
let color = ['blue', 'green', 'yellow', 'white']
for (let i = 1; i <= num; i++) {
if (i <= aver) { //画方形
let wh = [[10, 2], [2, 4], [7,8]][Math.floor((Math.random()*3))]
let square = GraphicsFactory.create('square')
//具体场景数据,不同尺寸、颜色
square.setParams({
w: wh[0],
h: wh[1],
color: color[Math.floor(Math.random()*color.length)]
})
square.draw(DrawTools)
}else if (i <= 2 * aver) { //圆形
let radius = [1, 2, 3, 5, 9]
let circle = GraphicsFactory.create('circle')
circle.setParams({
radius: radius[Math.floor(Math.random()*radius.length)],
color: color[Math.floor(Math.random()*color.length)]
})
circle.draw(DrawTools)
}else if (i <= 3 * aver) { //椭圆形
let ab = [[1, 2], [3, 4], [6, 8]][Math.floor((Math.random()*3))]
let ellipse = GraphicsFactory.create('ellipse')
ellipse.setParams({
a: ab[0],
b: ab[1],
color: color[Math.floor(Math.random()*color.length)]
})
ellipse.draw(DrawTools)
}else if(i <= 4 * aver) { //三角形
let xyz = [[6, 7, 8], [3, 4, 5], [2, 1, 2]]
let triangle = GraphicsFactory.create('triangle')
triangle.setParams({
x: xyz[0],
y: xyz[1],
z: xyz[2],
color: color[Math.floor(Math.random()*color.length)]
})
triangle.draw(DrawTools)
}
}
console.log(`用时 - ${new Date - startT}ms`)
}
drawDemo()
对象池
同享元模式相似,都是性能优化方案,不过不用分离内部状态和外部状态
对象池维护一个装载空闲对象的内存池,当需要对象完成业务时直接从对象池获取,否则创建新的对象,任务完成后将对象回收到对象池
常见应用如http连接池、数据库连接池,在web中常用来缓存DOM对象,减少DOM节点创建和删除
//页面显示5张图,一段时间后更换
function objectPoolFactory(fn) {
let _objectPool = []
return {
create() {
let obj = _objectPool.length == 0 ? fn.apply(null, arguments)
: _objectPool.shift()
return obj
},
recycle(obj) {
_objectPool.push(obj)
}
}
}
var ImgFactory = objectPoolFactory(() => {
let img = document.createElement('img')
document.body.appendChild(img)
img.onload = () => {
ImgFactory.recycle(img) //图片加载完成后回收对象
}
return img
})
for (let i = 0; i < 5; i++) {
var img = ImgFactory.create()
img.src = 'xx'+ i + '.jpg'
}
setTimeout(() => {
for (let i = 0; i < 5; i++) {
var img = ImgFactory.create()
img.src = 'yy'+ i + '.jpg'
}
}, 5000)
职责链模式
创建了一个处理请求的对象链,请求会沿着链依次传递直到被处理;请求发送者不需要关心接受者,只需将请求发送到链的第一个节点就行,请求发送者和接受者之间进行了解耦
示例:打印日志信息,其中每个日志模块只能打印级别不低于自己的信息,否者交给更低级别模块打印
//打印处理模块
const LogTools = {
error: {
level: 3,
log(message) {
console.error(`error - ${message}`)
}
},
warn: {
level: 2,
log(message) {
console.warn(`warn - ${message}`)
}
},
info: {
level: 1,
log(message) {
console.log(`info - ${message}`)
}
}
}
//职责链中节点类
function LogChain(logTool) {
this.level = logTool.level //节点处理日志级别
this.log = logTool.log //处理逻辑
this._nextChainNode = null //下个节点
}
LogChain.prototype.setNextChainNode = function(chain) {
this._nextChainNode = chain
}
//节点处理请求或是转发请求
LogChain.prototype.handleRequest = function(level, msg) {
'use strict';
if (this.level <= level) {
//非严格模式下,arguments和函数参数变量引用关联,此时msg会覆盖level;严格模式下非引用关联
[].shift.apply(arguments)
this.log.apply(this, arguments)
}
if (this._nextChainNode) {
//请求传递
this._nextChainNode.handleRequest(level, msg)
}
}
function chainDemo() {
let chain = _getChain()
chain.handleRequest(1, 'info log message.')
chain.handleRequest(2, 'warn log message.')
chain.handleRequest(3, 'error log message.')
//获取职责链
function _getChain() {
//在业务需要时进行节点组合,业务和功能模块解耦
let errorChain = new LogChain(LogTools.error)
let warnChain = new LogChain(LogTools.warn)
let infoChain = new LogChain(LogTools.info)
errorChain.setNextChainNode(warnChain)
warnChain.setNextChainNode(infoChain)
return errorChain
}
}
chainDemo()
// info - info log message.
// warn - warn log message.
// info - warn log message.
// error - error log message.
// warn - error log message.
// info - error log message.
AOP实现职责链
结合JavaScript函数式编程的特点,可以很灵活地实现一个职责链,缺点是闭包保存了各级函数的作用域,当链较长时会导致性能问题
//待续