设计原则

  • 单一职权原则(SRP)

    一个对象或方法只做一件事情。如果一个方法承担了过多的职责,那么在需求的变迁过程中,需要改写这个方法的可能性就越大。

    应该把对象或方法划分成较小的粒度

  • 最小知识原则(LKP)

    一个软件实体应当 尽可能少地与其他实体发生相互作用

    应当尽量减少对象之间的交互。如果两个对象之间不必彼此直接通信,那么这两个对象就不要发生直接的 相互联系,可以转交给第三方进行处理

  • 开放-封闭原则(OCP)

    软件实体(类、模块、函数)等应该是可以扩展的,但是不可修改

    当需要改变一个程序的功能或者给这个程序增加新功能的时候,可以使用增加代码的方式,尽量避免改动程序的源代码,防止影响原系统的稳定

什么是设计模式

假设有一个空房间,我们要日复一日地往里面放一些东西。最简单的办法当然是把这些东西直接扔进去,但是时间久了,就会发现很难从这个房子里找到自己想要的东西,要调整某几样东 西的位置也不容易。所以在房间里做一些柜子也许是个更好的选择,虽然柜子会增加我们的成本,但它可以在维护阶段为我们带来好处。使用 这些柜子存放东西的规则,或许就是一种模式

设计模式的原则是 “找出 程序中变化的地方,并将变化封装起来”,它的关键是意图,而不是结构。

单例模式

说明含义
定义保证一个类仅有一个实例,并提供一个访问它的全局访问点
核心确保只有一个实例,并提供全局访问
实现假设要设置一个管理员,多次调用也仅设置一次,我们可以使用闭包缓存一个内部变量来实现这个单例

通过定义核心,可以联想到JavaScript中的全局对象,利用ES6的let不允许重复声明的特性,刚好符合这两个特点;是的,全局对象是最简单的单例模式

let CreateSingleton = (function () {
  let instance = null
  return function (name) {
    this.name = name
    if (instance) {
      return instance
    }
    return (instance = this)
  }
})()

CreateSingleton.prototype.getName = function () {
  console.log(this.name)
}

let winner = new CreateSingleton('winner') //winner
winner.getName()
let sunner = new CreateSingleton('sunner') //winner
sunner.getName()

第二次实例化CreateSingleton对象时,由于已经存在了该实例,因此直接将实例返回。

还可以通过代理的形式,将创建对象的操作和实例判断的操作进行解耦拆分,实现更小粒度的划分,符合”单一职责原则“;

// 只负责判断是否需要创建对象
let ProxyCreateSingleton = (function () {
  let instance = null
  return function (name) {
    if (instance) {
      return instance
    }
    return (instance = new Singlton(name))
  }
})()

// 创建对象
let Singlton = function (name) {
  this.name = name
}
// 对象方法
Singlton.prototype.getName = function () {
  console.log(this.name)
}

工厂模式

什么是工厂:不暴露对象创建的逻辑,而是将逻辑封装在一个函数内,那么这个函数可以成为工厂

  1. 简单工厂
let factory = function (role) {
  function User(obj) {
    this.name = obj.name
    this.role = obj.role
  }
  switch (role) {
    case 'superman':
      return new User({ name: '平台用户', role: ['主页', '登录页'] })
      break
    case 'man':
      return new User({ name: '游客', role: ['登录页'] })
      break
    default:
      throw new Error('参数错误')
  }
}

let superman = factory('superman')
let man = factory('man')
  1. 工厂方法
let factory = function (role) {
  if (this instanceof factory) {
    var s = new this[role]()
    return s
  } else {
    return new factory(role)
  }
}

factory.prototype = {
  admin: function () {
    this.name = '平台用户'
    this.role = ['登录页', '主页']
  },
  common: function () {
    this.name = '游客'
    this.role = ['登录页']
  },
  test: function () {
    this.name = '测试'
    this.role = ['登录页', '主页', '测试页']
    this.test = '我还有一个测试属性哦'
  }
}

let admin = new factory('admin')
let common = new factory('common')
let test = new factory('test')

工厂方法:对于类的扩充优于修改 继承优于扩充

  1. 安全模式下

    var Demo = function () {
      if (!(this instanceof Demo)) {
        return new Demo()
      }
    }
    
    Demo.prototype = {
      show: function () {
        console.log(123)
      }
    }
    
    var d = new Demo()
    d.show()
    var d = Demo()
    d.show()
  2. 抽象工厂

    抽象工厂的作用是让子类继承父类,但子类必须实现父类没有实现的功能。

class Car {
  getPrice() {
    return new Error('抽象方法不能调用')
  }
  getSpeed() {
    return new Error('抽象方法不能调用')
  }
}

class BMW extends Car {
  constructor(price, speed) {
    super()
    this.price = price
    this.speed = speed
  }
  // 子类必须实现父类没有实现的方法
  getPrice() {
    return this.price
  }
  getSpeed() {
    return this.speed
  }
}
const bmw = new BMW(1, 2)
console.log(bmw.getPrice())

外观模式

说明含义
定义为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使子系统更加容易使用
核心可以通过请求外观接口来达到访问子系统,也可以选择越过外观来直接访问子系统
实现外观模式在 JS 中,可以认为是一组函数的集合
// 三个处理函数
function start() {
  console.log('start')
}

function doing() {
  console.log('doing')
}

function end() {
  console.log('end')
}

// 外观函数,将一些处理统一起来,方便调用
function execute() {
  start()
  doing()
  end()
}

// 调用init开始执行
function init() {
  // 此处直接调用了高层函数,也可以选择越过它直接调用相关的函数
  execute()
}

init() // start doing end

适配器模式

说明含义
定义是解决两个软件实体间的接口不兼容的问题,对不兼容的部分进行适配
核心解决两个已有接口之间不匹配的问题
实现比如一个简单的数据格式转换的适配器
var A = A || {}
A.g = function (id) {
  return document.getElementById(id)
}
A.on = function (id, type, fn) {
  var dom = typeof id === 'string' ? this.g(id) : id
  if (dom.addEventListener) {
    dom.addEventListener(type, fn, false)
  } else if (dom.attachEvent) {
    dom.attachEvent('on' + type, fn)
  } else {
    dom['on' + type] = fn
  }
}
A.on(window, 'load', function () {
  A.on('mybutton', 'click', function () {
    alert(123)
  })
})

装饰器模式

说明含义
定义以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象。
是一种 “即用即付” 的方式,能够在不改变对 象自身的基础上,在程序运行期间给对象动态地添加职责
核心是为对象动态加入行为,经过多重包装,可以形成一条装饰链
实现获取该元素已绑定的事件,调用后在调用扩充的事件
var decorator = function (input, fn) {
  // 我想要对 input 的点击事件 进行扩充,首先要获取原有的功能
  var input = document.getElementById(input)
  if (typeof input.onclick === 'function') {
    var oldClickEvent = input.onclick
    input.onclick = function () {
      oldClickEvent()
      fn()
    }
  } else {
    input.onclick = fn
  }
}

decorator('tel_input', function () {
  telDemoText.style.display = 'none'
})

参考

同类推荐