一、发布订阅模式
定义:又称观察者模式,他定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于他的对象都将都到统治
let event = {
clientList:{},
listen:function(key,fn){
if(!this.clientList[key]){
this.clientList[key] = []
}
this.clientList[key].push(fn)
},
trigger:function(){
let key = Array.prototype.shift.call(arguments)
let fns = this.clientList[key]
if(!fns||fns.length){
return false
}
for(let i = 0,fn;fn=fns[i++]){
fn.apply(this,arguments)
}
}
}
event.remove = function(key,fn){
let fns = this.clientList[key]
if(!fns){
return false
}
if(!fn){
fns&&((fns.length=0))//如果没有传入具体的回调函数,表示需要取消key对应消息的所有订阅
}else{
for(let l= fns.length;l>=0;l++){
let _fn = fns[1]
if(_fn === fn){
fns.splice(l,1)
}
}
}
}
let installEvent = function(obj){ //给对象动态安装发布订阅功能
for(let i in obj){
installEvent[i] = obj[i]
}
}
二、组合模式
组合模式将对象组合成树形结构,以表示"部分-整体"的层次结构,除了用来表示树形结构之外,组合模式的另一个好处是通过对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性
①表示属性结构:提供了一种便利树形结构的方案,通过调用组合的execute方法,程序会递归调用组合对象下面的也对象的execute方法,所以我们只需要一次操作即可完成几件事。组合模式可以方便的描述对象部分--整体层次结构。
②利用对象多态性同意对待组合对象和单个对象。利用对象的多态性表现,可以使客户端忽略组合对象和单个对象的不同。
在实际的开发中,这会带来极大的便利,当向组合模式里添加命令时,只要它是一个命令且有可执行的execute方法,那么这个命令就可以被添加进万能遥控器。
宏命令是一组命令的集合,通过执行宏命令的方式,可以一次执行一批命令。
let MacroCommond = function(){
return {
commandList:[],
add:function(command){
this.commandList.push(command)
},
execute:function(){
for(let i=0,command;command = this.commandList[i++];){
command.execute()
}
}
}
}
let openAcCommand = {
execute:function(){
console.log('打开空调')
}
}
let openTvCommand = {
execute:function(){
console.log('打开电视')
}
}
let macroCommand1 = MacroCommond()
macroCommand1.add(openAcCommand)
macroCommand1.add(openTvCommand)
macroCommand1.execute()
宏命令可以包含宏命令组成一个"超级命令"
Folder.prototype.add = function(file){
file.parent = this
this.files.push(file) //父节点引用
}
Folder.prototype.scan = function(){
console.log('开始扫描文件'+this.name)
for(let i=0,file,files=this.files;file = files[i++];){
file.scan()
}
}
Folder.prototype.remove = function(){
if(!this.parent){
return
}
for(let files = this.parent.files,l=files.length-1;l>=0;l--){
let file = files[l]
if(file===this){
files.splice(l,1)
}
}
}
三、模板方法模式
模板方法模式是一种只需要使用继承就可以实现的非常简单的模式。
模板模式是一种典型的通过封装变化提高系统拓展性的设计模式。
模板方法由两部分组成,第一部分是抽象父类,第二部分是具体的实现子类。通常在抽象父类中封装了子类的算法框架,也包括一些公共方法以及封装在子类中所有方法的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。
Ⅰ、js没有抽象类的缺点和解决方案
缺点:当使用原型继承来模拟传统的类式继承时,并没有编译器帮助我们进行任何形式的检查,并不能保证子类会重写父类中的“抽象方法”
解决方案:
1、使用鸭子类型来模拟接口检查,但会带来不必要的复杂性
2、让Beverage等方法直接抛出一个异常,如果粗心忘记编写,在程序运行时会报错
Ⅱ、UI组件一般构建过程
①初始化一个div容器哦
②通过ajax请求拉取相应的容器哦
③数据渲染到div容器里面,完成组件的构造
④通知用户组建渲染完成
于是我们可以把这4个步骤都抽象到父类的模板方法里面,父类还以顺便提供第一步和第四步的具体实现。当子类继承这个父类后,会重写模板方法里的第二步和第三步
Ⅲ、钩子方法
放置钩子是个例变化的一种常见手段。我们在父类中容易变化的地方放置钩子,钩子可以有一个默认的实现。究竟要不要实现"挂钩",由子类自行决定。
let Breverage = function(){}
Breverage.prototype.boilWater = function(){
console.log("把水煮沸")
}
Breverage.prototype.brew = function(){
throw new Error("子类必须重写pourInbrew方法")
}
Breverage.prototype.porInCup = function(){
throw new Error("子类必须重写porInCup方法")
}
Breverage.prototype.addCondiments = function(){
throw new Error("子类必须重写addCondiments方法")
}
Breverage.prototype.addCondiments = function(){
throw new Error("子类必须重写addCondiments方法")
}
Breverage.prototype.customerWantsCondiments = function(){ //是否需要调料
throw new Error("子类必须重写addCondiments方法")
}
Breverage.prototype.init = function(){
this.boilWater()
this.brew()
this.porInCup()
if(this.customerWantsCondiments()){
this.addCondiments()
}
}
let CoffeeWithHook = function(){}
CoffeeWithHook.prototype = new Breverage()
CoffeeWithHook.prototype.brew = function(){
console.log('用沸水冲泡咖啡')
}
let coffeeWithHook = new CoffeeWithHook()
coffeeWithHook.init()
//也可不通过继承实现
let Breverage = function(param){
let boilWater = function(){
console.log("把水煮沸")
}
let brew = param.brew || function(){
throw new Error("子类必须重写pourInbrew方法")
}
let porInCup = param.porInCup || function(){
throw new Error("子类必须重写porInCup方法")
}
let addCondiments = param.addCondiments || function(){
throw new Error("子类必须重写addCondiments方法")
}
let customerWantsCondiments = param.customerWantsCondiments || function(){ //是否需要调料
throw new Error("子类必须重写addCondiments方法")
}
let F = function(){}
F.prototype.init = function(){
boilWater()
brew()
addCondiments()
customerWantsCondiments()
}
return F
}
let Coffee = Breverage({
brew:function(){
console.log('用沸水冲咖啡')
},
porInCup:function(){
console.log('把咖啡倒进杯子')
}
})
let coffee = new Coffee()
coffee.init()
let Tea = Breverage({
brew:function(){
console.log('用沸水冲茶叶')
},
porInCup:function(){
console.log('把茶倒进杯子')
}
})
let tea = new Tea()
coffteae.init()
四、职责链模式
·定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,知道有一个对象处理它为止。
应用:①
let Chain = function(fn){
this.fn = fn
this.successor = null
}
Chain.prototype.setNextSuccessor = function(successor){
this.successor = successor
}
Chain.prototype.passRequest = function(){ //接受传入的请求
let ret = this.fn.apply(this,arguments)
if(ret === 'nextSuccessor'){
return this.successor && this.successor.passRequest.apply(this.successor,arguments)
}
return ret
}
let order500 = function(orderType,pay,stock){
if(orderType ===1 && pay === true){
console.log('500定金预购,享100优惠卷')
}else{
return 'nextSuccessor'
}
}
let order200 = function(orderType,pay,stock){
if(orderType ===2 && pay === true){
console.log('200定金预购,享100优惠卷')
}else{
return 'nextSuccessor'
}
}
let orderNormal = function(orderType,pay,stock){
if(stock>0 ){
console.log('普通购买,无优惠卷')
}else{
console.log('手机库存不足')
}
}
//将三个订单函数包装成职责链的节点
let chainOrder500 = new Chain( order500 )
let chainOrder200 = new Chain( order200 )
let chainOrderNormal = new Chain( orderNormal )
//指定节点再职责链中的顺序
chainOrder500.setNextSuccessor(chainOrder200)
chainOrder200.setNextSuccessor(chainOrderNormal)
//将请求传递给第一个节点
chainOrder500.passRequest(2,true,0)
//灵活添加、移除和修改链中的节点顺序,如下
let order300 = function(){
//具体实现
}
let chainOrder300 = new Chain(order300)
chainOrder500.setNextSuccessor(chainOrder300)
chainOrder300.setNextSuccessor(chainOrder200)
②异步的职责链
有事需要根据异步请求返回结果才能决定是否继续在职责链中passRequest(接收传入的参数)
所以需要给china添加一个原型方法China.prototype.next()
China.prototype.next = function(){
return this.successor && this.successor.passRequest.apply(this.successor,arguments)
}
③用AOP实现职责链
Function.prototype.after = function(fn){
let self = this
return function(){
let ret = self.apply(this,arguments)
if(ret === 'nextSuccessor'){
return fn.apply(this,arguments)
}
return ret
}
}
let order = order500.after(order200).after(orderNormal)