今天看啥  ›  专栏  ›  弑晓风

常用的几种设计模式

弑晓风  · 掘金  ·  · 2019-11-27 08:46

文章预览

阅读 154

常用的几种设计模式

You can walk as far as you want. 
海阔凭鱼跃,天高任鸟飞。

引言

问:什么是设计模式?

答:设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性可维护性可读性稳健性以及安全性的解决方案。

网上还有一种更好的说法:

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

它和语言无关,它表达的是一种思想。

这里以js为例分享了几种常用的设计模式,后续会有补充。

所有源码均已上传至github:传送门


策略模式(strategy)

在了解策略模式前先举个例子:

马上就要到年底了,很多公司的年终奖是根据员工的工资基数和年底绩效情况来发放的。

  • 绩效为A的人年终奖有4倍工资
  • 绩效为B的人年终奖有3倍工资
  • 绩效为C的人年终奖是2倍工资。
  • 绩效为D的人年终奖是1倍工资。

假设财务部要求我们提供一段代码,来方便他们计算员工的年终奖。

假设工资基数为1000.

let base = 1000;复制代码

常规思路

let calculate1 = function(salary,level) {
  if(level === 'A') {
      return salary * 4;
  } else if(level === 'B') {
      return salary * 3;
  } else if(level === 'C') {
      return salary * 2;
  } else if(level === 'D') {
    return salary * 1;
  }
};复制代码

点评:虽然功能实现了,但是这代码明显包含了太多if-else,简直不忍直视。首先扩展性很差,后续如果再加个EFG或者是将绩效基数调整一下,需要直接改动该方法的内部实现,这明显违背了开闭原则,非常不友好;其次如果有别的地方也用到了类似计算绩效的方式,但是规则不一样,这不够通用。

ps:开闭原则简单讲就是对扩展开放,对修改关闭。

使用组合函数重构代码

let salaryA = salary => salary * 4;let salaryB = salary => salary * 3;let salaryC = salary => salary * 2;let salaryD = salary => salary * 1;let calculate2 = function(salary,level) {  if(level === 'A') {      return salaryA(salary);  } else if(level === 'B') {      return salaryB(salary);  } else if(level === 'C') {      return salaryC(salary);  } else if(level === 'D') {    return salaryD(salary);  }};

复制代码

点评将计算绩效的算法统统封装了起来,看起来相比常规思路有所改善,只是使计算绩效的方法更加通用 ,扩展性还是不行。

使用策略模式重构代码

let strategies = {  'A': salary => salary * 4,  'B': salary => salary * 3,  'C': salary => salary * 2,  'D': salary => salary * 1};let calculate3 = function(salary,level) {  return strategies[level](salary);};复制代码

总结:策略模式指的是定义一系列的算法,并且把它们封装起来,但是策略模式不仅仅只封装算法,我们还可以对用来封装一系列的业务规则,只要这些业务规则目标一致,我们就可以使用策略模式来封装它们。

(同理表单校验,这里不做阐述)

测试代码及结果

console.log('resultA = ', calculate3(base,'A'));console.log('resultB = ', calculate3(base,'B'));console.log('resultC = ', calculate3(base,'C'));console.log('resultD = ', calculate1(base,'D'));复制代码


单例模式

单例模式老生常谈了,顾名思义就是一个类只有一个实例。

特点

  1. 确保自己只有一个实例。
  2. 必须自己创建自己的实例。
  3. 必须为其他对象提供唯一的实例。

优点

  1. 节省内存,提高访问速度
  2. 更加灵活更改类的实例化过程
  3. 避免全局使用的类频繁地创建与销毁
  4. 避免对资源的多重占用(比如写文件操作)

缺点

与单一职责原则冲突(一个类应该只关心内部逻辑,而不关心外面怎么样来实例化)

不适用于变化的对象

ps:单一职责原则就是一个类/接口/方法只负责一项职责或职能

实现方式 

  1. 饿汉单例 (顾名思义,还未使用到该类,但是先实例化)
  2. 懒汉单例(顾名思义,用到时再实例化)

饿汉单例

let instance = null;class Printer {  constructor() {    console.log('我是一台打印机');  }  static getInstance() {    if (instance === null) {      console.log('初始化打印机...');      instance = new Printer();    }    return instance;  }  print(){    console.log('打印中......');  }}复制代码

测试代码及结果

var printer  = Printer.getInstance();//多 new 一次printer = Printer.getInstance();printer.print();复制代码

复制代码

懒汉单例

let LazyInstance = function(fn) {  let result = null;  return function() {    result || (result = fn.apply(this, arguments));  }};复制代码

测试代码及结果

let myPrinter = function () {  console.log('初始化一台打印机...');};let lazyInstance = LazyInstance(myPrinter);lazyInstance();复制代码


工厂模式

实现方法

  1. 简单工厂模式
  2. 工厂方法模式 (实现子类继承父类,不适用js)
  3. 抽象工厂模式 (不直接生成实例, 而是用于对产品类簇的创建)

因为后面两种工厂模式使用较少,这里以简单工厂模式为例。简单工厂模式又叫静态工厂模式,由一个工厂对象决定创建某一种产品对象类的实例。主要用来创建同一类对象。它主要适用于创建的对象数量较少,对象的创建逻辑不复杂时使用。说白了,它就是把你new权限收回,由它来统一实例化。

简单工厂模式

这里以动物为例,由动物工厂批量生产动物。

class Animal {  constructor (type) {    this.type = type;  }  setName(name) {    this.name = name;  }  // getName() {  //   return this.name;  // }  detail() {    if (!this.name) {      console.log(`First of all, let's give its a name. `);      return;     }    console.log(`this is a ${this.type},its name is ${this.name}`);  }  static getInstance(type) {    return new Animal(type);  }}复制代码

测试代码及结果

let cat = Animal.getInstance('cat');cat.setName('tom');cat.detail();复制代码


未完待续....

end


您的点赞和关注是对我最大的支持,谢谢!


………………………………

原文地址:访问原文地址
快照地址: 访问文章快照
总结与预览地址:访问总结与预览