设计模式 --kkb

设计模式概念

前人总结的代码最佳实践。

设计模式是一套被反复使用、多人知晓的、经过分类的、代码设计经验的总结。

 

工程师基础(内力,决定走多远)

算法和数据结构、设计模式、网络协议(TCP/IP 通信 缓存)、操作系统、计算机组成、数据库(和数据结构也息息相关,红黑树)、编译原理(vue的compile模块)

 

订阅/发布模式(观察者模式)

class Event{
  constructor(){
    this.callbacks = {}
  }
  $on(name, fn){ // 监听
    // if(!this.callbacks[name]){
    //   this.callbacks[name] = []
    // }
    // this.callbacks[name].push(fn)

    // 上述代码的简写
    (this.callbacks[name] || (this.callbacks[name]=[])).push(fn)
  }
  $emit(name, arg){
    const cbs = this.callbacks[name]
    if(cbs){
      cbs.forEach(c=>{
        c.call(this, arg)
      })
    }
  }
  $off(name){
    this.callbacks[name] = null
  }
}

let event = new Event()
event.$on('event1', arg => {
  console.log('event1触发', arg)
})
event.$on('event1', arg => {
  console.log('又一个event1触发', arg)
})
event.$on('event2', arg => {
  console.log('event2触发', arg)
})

event.$emit('event1', 'myArg')
event.$emit('event2', 'myArg2')
event.$off('event1')
event.$emit('event1', 'myArg')

 

单例模式

定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。实现的方法为先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。

适用场景:一个单一对象。比如:弹窗,无论点击多少次,弹窗只应该被创建一次,实现起来也很简单,用一个变量缓存即可

 

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    .modal{
      position: fixed;
      border: 2px solid red;
      width: 300px;
      height: 300px;
      top: 30%;
      left: 50%;
      margin-left: -150px;
      text-align: center;
      padding: 20px;
    }
  </style>
</head>
<body>
  <div id="model-btn">点我</div>

  <script>
    function getSingle(fn){
      // 使用闭包存储期望的单例实例
      // 把函数fn变成单例
      let result
      return function(){
        return result || (result=fn.apply(this, arguments))
      }
    }
    function createModalLayer(){
      console.log('新建弹窗')
      // 期望弹窗是全局唯一的
      let div = document.createElement('div')
      div.innerHTML = '我是一个弹窗'
      div.className = 'modal'
      div.style.display = 'none'
      div.addEventListener('click', function(){
        div.style.display = 'none'
      }, false)
      document.body.appendChild(div)
      return div
    }

    // 只需要对原本的函数包一层,就变成单例了
    createModalLayer = getSingle(createModalLayer)

    document.getElementById('model-btn').addEventListener('click', function(){
      // 新建一个弹窗
      const modalLayer = createModalLayer()
      // 修改弹窗的内容
      modalLayer.style.display = 'block'
      modalLayer.innerHTML = '登录' + new Date()
    }, false)
  </script>
</body>
</html>

 

策略模式

// 判断绩效:绩效S为4倍工资,A为3倍工资,B为2倍工资

function calculate(level, salary){
  if(level==='S'){
    return salary*4
  }
  if(level==='A'){
    return salary*3
  }
  if(level==='B'){
    return salary*2
  }
  return salary
}

上述代码如果想加入新的条件,比如: SS绩效 5倍工资

就得多加一条 if 判断

if(level==='SS'){
  return salary*5
}

这种我们称为面条代码,不好维护而且阅读起来比较困难

 

将至改造成策略模式(策略 + 使用)

// 新的策略,就扩展这个对象---这里可以使用配置 {"S":4, "A":3, "B":2} ,也可以是接口返回
const policy = {
  'S': function(salary){
    return salary * 4
  },
  'A': function(salary){
    return salary * 3
  },
  'B': function(salary){
    return salary * 2
  }
}

// 使用层(业务层)
function calculate(level, salary){
  return policy[level] ? policy[level](salary) : salary
}

 

代理模式

代理模式的定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。

常用的虚拟代理形式:某一个花销很大的操作,可以通过虚拟代理的方式延迟到这种需要它的时候采取创建(例:使用虚拟代理实现图片懒加载)

图片懒加载的方式:先通过一张loading图占位,然后通过异步的方式加载图片,等图片加载好了再把完成的图片加载到img标签里面

 

原本的代码:

// 图片实例有一个setSrc方法,会先设置loading,然后再显式图片
let imgFunc = (function(){
  let imgNode = document.createElement('img')
  document.body.appendChild(imgNode)
  return {
    setSrc: function(src){
      imgNode.src = src
    }
  }
})()

imgFunc.setSrc('https://res.kaikeba.com/other/123/20191209112316-88923/FvFBVu8ZVu5aLU6yWTvGn8dwrjhm.png')

 

使用代理后

// 图片实例有一个setSrc方法,会先设置loading,然后再显式图片
let imgFunc = (function(){
  let imgNode = document.createElement('img')
  document.body.appendChild(imgNode)
  return {
    setSrc: function(src){
      // 图片较大时会比较耗时
      imgNode.src = src
    }
  }
})()

// 使用代理模式,增加loading的功能
let proxyImage = (function(){
  let img = new Image()
  img.onload = function(){
    // onload加载完毕,再设置
    imgFunc.setSrc(this.src)
  }
  return {
    setSrc(src){
      // 先设置loading
      imgFunc.setSrc('./loading.jpg')
      img.src = src
    }
  }
})()
// 使用proxyImage代理操作,设置src
proxyImage.setSrc('https://res.kaikeba.com/other/123/20191209112316-88923/FvFBVu8ZVu5aLU6yWTvGn8dwrjhm.png')

 

中介者模式

定义:通过一个中介者对象,其他所有的相关对象都通过该中介者对象来通信,而不是相互引用,当其中一个对象发生改变时,只需要通知中介者对象即可。通过中介者模式可以解除对象与对象之间的紧耦合关系。

适用的场景:例如购物车需求,存在商品选择表单、颜色选择表单、购买数量表单等待,都会触发change事件,那么可以通过中介者来转发处理这些事件,实现各个事件间的解耦,仅仅维护中介者对象即可。

redux,vuex都属于中介者模式的实际应用,我们把共享的数据,抽离成一个单独的 store,每个都通过 store 这个中介来操作对象。

 

装饰器模式

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

posted @ 2019-12-11 14:55  落叶无痕~  阅读(337)  评论(0编辑  收藏  举报