基本设计模式

Posted Smile沛沛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基本设计模式相关的知识,希望对你有一定的参考价值。

设计模式:针对特定的场景,在软件设计过程中常见的代码范式(通用解决方案,与语言无关)。

  • 观察者模式(麦当劳点餐)
  • 代理模式(花店送花)
  • 装饰器:可以允许向一个对象添加新的功能,却不改变原有对象。比如:
    • 给文件上传功能添加日志输出功能
    • react中redux的@connect
代理模式
  • 特点:代理对象与真实对象有相同的行为
  • 真实对象->代理对象->用户
  • 花店送花:男孩让花店送花给女孩
//定义女孩对象
var Girl = function(name){
  this.name = name;		
}

var Boy = function(girl){
	//女同学
  this.girl = girl;
  
  //送花
  this.sendGift = function(gift){
    console.log(this.girl.name+gift)	//花店下订单,花店人去送花
  }
}

//代理对象 花店员工
var ProxyObj = function(girl){
  this.girl = girl;					//需要知道女孩信息
  this.sendGift = function(gift){
    (new Boy(this.girl)).sendGift(gift)		//替人送花;直接调用
  }
}


//调用 不能看出是谁送花 
var girl = new Girl('Vicky')
var proxy = new ProxyObj(girl)
proxy.sendGift("flowers")
  • 代理模式的应用:图片懒加载
    • 真实图片:大、加载慢;代理图片:小,加载快
//常用图片懒加载
window.onload = function(){
  var myImage = (function(){			//自执行函数
    var imgNode = document.createElement('img')		//创建真实图片节点
    document.body.appendChild(imgNode)		//图片节点加到body
    var img = new Image()									//代理对象 先展示代理图片 接着拉取实际图片
    img.onload = function(){					//真实图片加载完毕后触发
      imgNode.src = this.src;					//把真实图片url给真实图片节点;替换等待图片;this指向代理对象
    }
    return {					 //返回一个对象
      setSrc:function(src){
        imgNode.src = 'https://th.bing.com/th/id/R20d0cd9f696181bbead3b250782239fc?rik=SozVXnglW3zBxg&riu=http%3a%2f%2fbpic.588ku.com%2felement_pic%2f01%2f35%2f08%2f79573bd17084b13.jpg&ehk=UPH3Ji9JG4ZMqBzH9qKIJwuFG2R31XoHzvp4NkK5vXc%3d&risl=&pid=ImgRaw';		//代理图片url给到真实对象;先展示等待图片
        img.src = src; 		//把src给到代理对象
      }
    }
  })()
  //调用:把真实图片给到对象
 	myImage.setSrc("https://himg.bdimg.com/sys/portrait/item/pp.1.d64a7775.reheWDHF3vrjPLK3n-YgKw.jpg?tt=1623053822499");			//真实图片

}

使用代理模式重构图片懒加载

window.onload = function(){
  var myImage = (function(){
    var imgNode = document.createElement('img')
    document.body.appendChild(imgNode)
    return {
      setSrc:function(src){
         imgNode.src = src;
      }
    }
  })()
  
  //代理对象:先展示等待图片,再拉取真实图片
  var ProxyImage = (function(){
    var img = new Image()				//内存中的代理
    img.onload = function(){			//加载完之后将触发这个方法
      myImage.setSrc(this.src)		//this指向img;将真实图片给真实对象展示 
    }
    return {
      setSrc:function(src){
        myImage.setSrc('https://th.bing.com/th/id/R20d0cd9f696181bbead3b250782239fc?rik=SozVXnglW3zBxg&riu=http%3a%2f%2fbpic.588ku.com%2felement_pic%2f01%2f35%2f08%2f79573bd17084b13.jpg&ehk=UPH3Ji9JG4ZMqBzH9qKIJwuFG2R31XoHzvp4NkK5vXc%3d&risl=&pid=ImgRaw')	//代理图片
        img.src = src;
      }
    }
  })()
  
//调用;直接给代理对象设置真实图片  ProxyImage.setSrc('https://himg.bdimg.com/sys/portrait/item/pp.1.d64a7775.reheWDHF3vrjPLK3n-YgKw.jpg?tt=1623053822499')	//真实图片
}
观察者模式(发布订阅模式)

场景:双十一购买商品。购买者:观察者;商品:被观察者;商家(商品价格修改):发布者。发布者发布,关注着会收到改变。

观察者、被观察者、发布者

//观察者
var shopObj = {}

//商品列表 ['huawei':['订阅人1''订阅人2'],'apple':['订阅人3''订阅人2'],'oppo':['订阅人1']]
shopObj.list = []


//订阅;根据特定的商品推给特定人
shopObj.listen = function(goodskey,fn){		//商品订阅方法;fn订阅的行为; 建立商品和观察者的联系
  if(!this.list[goodskey]){
    this.list[goodskey] = []
  }
  shopObj.list[goodskey].push(fn)	//往特定商品列表中添加订阅
}

shopObj.publish = function(){
	var goodskey  = arguments[0]
	var fns = this.list[goodskey]
  for(var i = 0,fn;fn = fns[i++];){
    //执行订阅的fn
    
    fn.apply(this,arguments)
  }
}

//用户1:添加订阅
shopObj.listen('huawei',function(brand,model){
  console.log('user 1'+brand,model)
})

//用户2:添加订阅
shopObj.listen('apple',function(brand,model){
  console.log('user 2'+brand,model)
})


//商家发布订阅
shopObj.publish('huawei','P40')

观察者模式优化

//订阅发布放一起;之后再初始化
var event = {
  list:[],
  listen:function(key,fn){
  	if(!this.list[key]){
    	this.list[skey] = []
    }
    shopObj.list[key].push(fn)	//往特定商品列表中添加订阅
  },
  publish:function(){
    var goodskey  = arguments[0]
    var fns = this.list[goodskey]
    for(var i = 0,fn;fn = fns[i++];){
      //执行订阅的fn

      fn.apply(this,arguments)
    }
  }
}

//观察者对象初始化
var initEvent = function(obj){
  for(var i in event){
    obj[i] = event[i];
  }
}

//定义空发布者
var shopObj = {}			//空对象
initEvent(shopObj)		//将我们定义的方法属性都给它



//用户1:添加订阅
shopObj.listen('huawei',function(brand,model){
  console.log('user 1'+brand,model)
})

//用户2:添加订阅
shopObj.listen('apple',function(brand,model){
  console.log('user 2'+brand,model)
})


//商家发布订阅
shopObj.publish('huawei','P40')
装饰器
  • 给对象添加额外行为但是没有改变原有对象

  • 简单装饰器的实现

class Circle{
  draw(){
    console.log('draw a circle')
  }
}

//使用装饰器添加一个边框
class Decorator{
  constructor(circle){
    this.circle = circle;
  }
  draw(){
    this.circle.draw()		//圆自己绘制
    this.setBorder()			//增加绘制方法
  }
  setBorder(circle){				//装饰器方法
    console.log('draw border')
  }
}

var circle = new Circle()
var dec = new Decorator(circle)
dec.draw()
  • 装饰器–注解形式
class Boy {
	@run
  speak(){
    console.log('speak')
  }
}

function run(target,key,descriptor){			//装饰器参数。
	//target是Boy对象;key是被装饰的方法speak;descriptor是描述方法(writeable可写;enumerable可枚举,即是否可以for循环出来;configuerable可配置)
  console.log('run')
}

var boy = new Boy()
boy.speak()

需要配置babel,使用transform-decoarors-legacy@转化成ES5代码;安装之后使用babel xxx.js -o /build/xxx.js进行转化。

cnpm i -S babel-cli babel-preset-env babel-plugin-transform-decoarors-legacy

//babel-preset-env用于ES6转ES5
//babel-plugin-transform-decoarors-legacy用于注解翻译的

  • 装饰器参数
    • target 对象
    • key被装饰的方法
    • descriptor 描述对象
//给方法添加日志输出功能;不改变原有功能,添加新功能
class Math{
	@log
  add(a,b){
    return a+b;
  }
}

//日志装饰器
function log(target,name,descriptor){
  var oldValue = descriptor.value;			//descriptor.value是被修饰的方法,add
  descriptor.value = function(){
    console.log(`调用${name}`参数是`${arguments}`)	//arguments是js的内置对象,包含所有实参的类数组
    return oldValue.apply(target,arguments)
  }
  return descriptor;
}

var math = new Math();
math.value(1,3)

装饰器添加参数

//给方法添加日志输出功能,并且带上参数,加到a、b上;
class Math{
	@log(100)
  add(a,b){
    return a+b;
  }
}

//日志装饰器
function log(num){
  return function log(target,name,descriptor){
  	var _num = num || 0;
    var oldValue = descriptor.value;			
    descriptor.value = function(...args){
    	arg[0] += _num;		//改变原有参数值
    	arg[1] += _num;
      console.log(`调用${name}`参数是`${args}`)	
      return oldValue.apply(target,args)
    }
    return descriptor;
  }
}

var math = new Math();
math.value(1,3)


职责链模式
  • 场景:充值抽奖

开闭原则:原有代码对现有需求时关闭的,对扩展时开放的

充值500抽100rmb;充值200抽20rmb;否则无奖

function(orderType,isPay,count){		//订单类型;是否支付成功;金额
	if(orderType === 1){					//500rmb
    if(isPay){
      console.log('中奖100rmb')
    }else{
      if(count>0){
        console.log('抽到纪念奖')
      }else{
        console.log('谢谢参与')
      }
    }
	}else if(orderType === 2){		//200rmb
    if(isPay){
      console.log('中奖20rmb')
    }else{
      if(count>0){
        console.log('抽到纪念奖')
      }else{
        console.log('谢谢参与')
      }
    }
	}else if(orderType === 3){
    console.log('谢谢参与')
	}
  
}


  • 使用职责链模式重构
function order500(orderType,isPay,count){
  if(orderType === 1 && isPay){					//500rmb
    if(isPay){
      console.log('中奖100rmb')
    }else{
    	//console.log('不关我的事,给下一个处理')
      order200(orderType,isPay,count)
    }
}

function order200(orderType,isPay,count){
  if(orderType === 2 && isPay){					//500rmb
    if(isPay){
      console.log('中奖100rmb')
    }else{
    	//console.log('不关我的事,给下一个处理')
      orderdefault(orderType,isPay,count)
    }
}

function orderdefault(orderType,isPay,count){
	if(count>0 && isPay){
     	console.log('抽到纪念奖')
	}else{
      console.log('谢谢参与')
	}
}
  • 使用职责链再次重构
function order500(orderType,isPay,count){
  if(orderType === 1 && isPay){					//500rmb
    if(isPay){
      console.log('中奖100rmb')
    }else{
    	//console.log('不关我的事,给下一个处理')
      return "nextSuccessor"
    }
}

function order200(orderType,isPay,count){
  if(orderType === 2 && isPay){					//500rmb
    if(isPay){
      console.log('中奖100rmb')
    }else{
    	//console.log('不关我的事,给下一个处理')
       return "nextSuccessor"
    }
}


function orderdefault(orderType,isPay,count){
	if(count>0 && isPay){
     	console.log('抽到纪念奖')
	}else{
      console.log('谢谢参与')
	}
}

//创建职责链关系对象
function Chain(fn){
  this.fn = fn;
  this.successor = null;
}

//设置下一个关系对象
Chain.prototype.setnextSuccessor =function(successor){
  this.successor = successor;
}

//传递给下一个
Chain.prototype.passRequest = function(){
  var ret = this.fn.apply(this,arguments)
  if(ret === 'nextSuccessor'){		//传给下一个
    this.successor && this.successor.passRequest.apply(this.successor,arguments)
  }
}

//实例化
var chain500 = new Chain(order500)
var chain200 = new Chain(order200)
var chaindefault = new Chain(orderdefault)
chain500.setnextSuccessor = chain200;
chain200.setnextSuccessor = chaindefault;

//调用
chain500.passRequest(1,true,100)		//类型1 支付成功 金额500

面向对象:抽象、多态、继承、封装

以上是关于基本设计模式的主要内容,如果未能解决你的问题,请参考以下文章

html PHP代码片段: - AJAX基本示例:此代码演示了使用PHP和JavaScript实现的基本AJAX功能。

发生配置更改时如何不重置我的片段?

用于从 cloudkit 检索单列的代码模式/片段

(转) Java中的负数及基本类型的转型详解

是否有在单个活动中处理多个片段的 Android 设计模式?

片段内带有基本适配器的列表视图