前言

今天主要介绍一下我们平常会经常用到的设计模式,设计模式总的来说有23种,而设计模式在前端中又该怎么运用呢,接下来主要对比较前端中常见的设计模式做一个介绍。

设计模式的定义

设计模式是在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。在不同的编程语言中,对设计模式的实现其实是可能会有区别的。比如java和javascript,在Java这种静态编译型语言中,无法动态地给已存在的对象添加职责,所以一般通过包装类的方式来实现装饰者模式。但在JavaScript这种动态解释型语言中,给对象动态添加职责是再简单不过的事情。这就造成了JavaScript语言的装饰者模式不再关注于给对象动态添加职责,而是关注于给函数动态添加职责。本篇将介绍以下几个比较常见的设计模式:

  • 工厂模式

  • 单例模式

  • 代理模式

  • 观察者模式

  • 策略模式

一、工厂模式

工厂模式是用来创建对象的一种最常用的设计模式,不暴露创建对象的具体逻辑,而是将将逻辑封装在一个函数中,那么这个函数就可以被视为一个工厂,工厂模式根据抽象程度的不同可以分为:简单工厂,工厂方法和抽象工厂,接下来,将对简单工厂和工厂方法在JavaScript中的运用举个简单的例子:

1)简单工厂
  简单工厂模式又叫静态工厂模式,由一个工厂对象决定创建某一种产品对象类的实例,主要用来创建同一类对象
  比如说,在实际的项目中,我们常常需要根据用户的权限来渲染不同的页面,高级权限的用户所拥有的页面有些是无法被低级权限的用户所查看,所以我们可以在不同权限等级用户的构造函数中,保存该用户能够看到的页面。

let UserFactory = function (role) {function SuperAdmin() {this.name = "超级管理员",this.viewPage = ['首页', '用户管理', '订单管理', '应用管理', '权限管理']  }function Admin() {this.name = "管理员",this.viewPage = ['首页', '订单管理', '应用管理']  }function NormalUser() {this.name = '普通用户',this.viewPage = ['首页', '订单管理']  }

switch (role) {case 'superAdmin':return new SuperAdmin();break;case 'admin':return new Admin();break;case 'user':return new NormalUser();break;default:throw new Error('参数错误, 可选参数:superAdmin、admin、user');  }}

//调用let superAdmin = UserFactory('superAdmin');let admin = UserFactory('admin') let normalUser = UserFactory('user')

总结:在上面的例子中,UserFactory就是一个简单工厂,在该函数中有3个构造函数分别对应不同的权限的用户,当我们调用工厂函数时,只需要传递superAdmin, admin, user这三个可选参数中的一个获取对应的实例对象

  • 优点:简单工厂的优点在于,你只需要一个正确的参数,就可以获取到你所需要的对象,而无需知道其创建的具体细节;

  • 缺点:在函数内包含了所有对象的创建逻辑(构造函数)和判断逻辑的代码,每增加新的构造函数还需要修改判断逻辑代码,我们的对象不是上面的3个而是30个或更多时,这个函数会成为一个庞大的超级函数,便得难以维护,简单工厂只能作用于创建的对象数量较少,对象的创建逻辑不复杂时使用;

(2)工厂方法

工厂方法模式的本意是将实际创建对象的工作推迟到子类中,这样核心类就变成了抽象类,但是在JavaScript中很难像传统面向对象那样去实现创建抽象类,所以在JavaScript中我们只需要参考它的核心思想即可,我们可以将工厂方法看作是一个实例化对象的工厂类

比如说上面的例子,我们用工厂方法可以这样写,工厂方法我们只把它看作是一个实例化对象的工厂,它只做实例化对象这一件事情,我们采用安全模式创建对象

//安全模式创建的工厂方法函数let UserFactory = function(role) {if(this instanceof UserFactory) {var s = new this[role]();return s;  } else {return new UserFactory(role);  }}

//工厂方法函数的原型中设置所有对象的构造函数UserFactory.prototype = {SuperAdmin: function() {this.name = "超级管理员",this.viewPage = ['首页', '用户管理', '订单管理', '应用管理', '权限管理']  },Admin: function() {this.name = "管理员",this.viewPage = ['首页', '订单管理', '应用管理']  },NormalUser: function() {this.name = '普通用户',this.viewPage = ['首页', '订单管理']  }}

//调用let superAdmin = UserFactory('SuperAdmin');let admin = UserFactory('Admin') let normalUser = UserFactory('NormalUser')

总结:在简单工厂中,如果我们新增加一个用户类型,需要修改两个地方的代码,一个是增加新的用户构造函数,一个是在逻辑判断中增加对新的用户的判断,而在抽象工厂方法中,我们只需要在UserFactory.prototype中添加就可以啦。

二、单例模式

定义:是保证一个类只有一个实例,并且提供一个访问它的全局访问点。
需求:一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的window对象、登录浮窗等。
实现:用一个变量标识当前是否已经为某个类创建过对象,如果是,则在下一次获取这个类的实例时,直接返回之前创建的对象。
优点:

  • 可以用来划分命名空间,减少全局变量的数量

  • 可以被实例化,且实例化一次,再次实例化生成的也是第一个实例

下面举个例子,在js中,我们可以使用闭包来创建实现这种模式:

var single = (function(){var unique;

function getInstance(){    // 如果该实例存在,则直接返回,否则就对其实例化if( unique === undefined ){            unique = new Construct();        }return unique;    }

function Construct(){// ... 生成单例的构造函数的代码    }

return {getInstance : getInstance    }})();

总结:在上面的代码中,我们可以使用single.getInstance来获取到单例,并且每次调用均获取到同一个单例,在我们平时的开发中,我们也经常会用到这种模式,比如当我们单击登录按钮的时候,页面中会出现一个登录框,而这个浮窗是唯一的,无论单击多少次登录按钮,这个浮窗只会被创建一次,因此这个登录浮窗就适合用单例模式。

三、代理模式

代理模式主要是为其他对象提供一种代理以控制对这个对象的访问,主要解决在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上,在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

代理模式最基本的形式是对访问进行控制,代理对象和另一个对象(本体)实现的是同样的接口,实际上工作还是本体在做,它才是负责执行所分派的任务的那个对象或类,代理对象所做的不外乎节制对本体的访问,代理对象并不会在另一对象的基础上添加方法或修改其方法,也不会简化那个对象的接口,它实现的接口与本体完全相同,所有对它进行的方法调用都会被传递给本体。

(function(){// 示例代码

// 目标对象,是真正被代理的对象function Subject(){}    Subject.prototype.request = function(){};

/**     * 代理对象     * @param {Object} realSubject [持有被代理的具体的目标对象]     */function Proxy(realSubject){this.realSubject = readSubject;    }Proxy.prototype.request = function(){this.realSubject.request();    };}());

总结:在上面的代码中,Proxy可以控制对真正被代理对象的一个访问,在代理模式中,比较常见的就是虚拟代理,虚拟代理用于控制对那种创建开销很大的本体的访问,它会把本体的实例化推迟到有方法被调用的时候,比如说,现在我们假设PublicLibrary的实例化很慢,不能在网页加载的时候立即完成,我们可以为其创建一个虚拟代理,让它把PublicLibrary的实例化推迟到必要的时候,比如说我们在前端中经常用到的图片懒加载,就可以用虚拟代理;

四、观察者模式

如果大家学过一些像vue,react这些框架,相信大家对观察者模式一定很熟悉,现在很多mvvm框架都用到了观察者模式这个思想,观察者模式又叫做发布—订阅模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知和更新,观察者模式提供了一个订阅模型,其中对象订阅事件并在发生时得到通知,这种模式是事件驱动的编程基石,它有利益于良好的面向对象的设计

定义:对象间的一种一对多的依赖关系。
需求:当一个对象的状态发生变化时,所有依赖于他的对象都将得到通知。
优点:时间上的解耦,对象之间的解耦。

实现:

  • 指定好谁充当发布者;

  • 给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者;

  • 发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数。

下面举个例子,比如我们给页面中的一个dom节点绑定一个事件,其实就可以看做是一种观察者模式:

document.body.addEventListener("click", function() {    alert("Hello World")},false )document.body.click() //模拟用户点击

总结:在上面的例子中,需要监听用户点击 document.body 的动作,但是我们是没办法预知用户将在什么时候点击的,因此我们订阅了 document.body 的 click 事件,当 body 节点被点击时,body 节点便会向订阅者发布 "Hello World" 消息。

策略模式

策略模式指的是定义一些列的算法,把他们一个个封装起来,目的就是将算法的使用与算法的实现分离开来,避免多重判断条件,更具有扩展性。

下面也是举个例子,现在超市有活动,vip为5折,老客户3折,普通顾客没折,计算最后需要支付的金额,如果不使用策略模式,我们的代码可能和下面一样:

function Price(personType, price) {//vip 5 折if (personType == 'vip') {return price * 0.5;    } else if (personType == 'old'){ //老客户 3 折return price * 0.3;    } else {return price; //其他都全价    }}

在上面的代码中,我们需要很多个判断,如果有很多优惠,我们又需要添加很多判断,这里已经违背了刚才说的设计模式的六大原则中的开闭原则了,如果使用策略模式,我们的代码可以这样写:

// 对于vip客户function vipPrice() {this.discount = 0.5;}

vipPrice.prototype.getPrice = function(price) {  return price * this.discount;}// 对于老客户function oldPrice() {this.discount = 0.3;}

oldPrice.prototype.getPrice = function(price) {return price * this.discount;}// 对于普通客户function Price() {this.discount = 1;}

Price.prototype.getPrice = function(price) {return price ;}

// 上下文,对于客户端的使用function Context() {this.name = '';this.strategy = null;this.price = 0;}

Context.prototype.set = function(name, strategy, price) {this.name = name;this.strategy = strategy;this.price = price;}Context.prototype.getResult = function() {console.log(this.name + ' 的结账价为: ' + this.strategy.getPrice(this.price));}

var context = new Context();var vip = new vipPrice();context.set ('vip客户', vip, 200);context.getResult();   // vip客户 的结账价为: 100

var old = new oldPrice();context.set ('老客户', old, 200);context.getResult();  // 老客户 的结账价为: 60

var Price = new Price();context.set ('普通客户', Price, 200);context.getResult();  // 普通客户 的结账价为: 200

总结:在上面的代码中,通过策略模式,使得客户的折扣与算法解藕,又使得修改跟扩展能独立的进行,不影到客户端或其他算法的使用。

当我们的代码中有很多个判断分支,每一个条件分支都会引起该“类”的特定行为以不同的方式作出改变,这个时候就可以使用策略模式,可以改进我们代码的质量,也更好的可以进行单元测试。

贡献者

  • IT实战联盟-Line

---------------END----------------

后续的内容同样精彩

长按关注“IT实战联盟”哦

5 修改request对象变量_【总结】前端5大常见设计模式,代码一看你就懂!相关推荐

  1. 打开word2007弹出未设置对象变量_【跟我学LabVIEW】什么是全局变量?如何创建及使用全局变量?...

    同C语言类似,LabVIEW中的变量根据作用域的不同也分为两种类型,即局部变量和全局变量,前者仅能在当前VI程序中使用,而后者可以在多个文件中使用. 本篇文章介绍全局变量的创建及使用方法. 什么是全局 ...

  2. 修改java环境变量_怎么配置java环境变量

    配置java环境变量的方法:1.右键点击[计算机],选择[属性],点击[高级系统设置],打开环境变量设置:2.新建[JAVA_HOME]变量,并编辑[Path]变量:3.新建[Classpath]变量 ...

  3. php 代码修改后 重新实例化_从匿名函数到PHP设计模式之容器模式

    点击蓝字关注我们!每天获取最新的编程小知识! 源 / php中文网      源 / www.php.cn 从匿名函数(闭包特性)到 PHP 设计模式之容器模式 (查看原文请点击本文末尾左下角: 匿名 ...

  4. linux内核怎么修改屏幕旋转方向_运维必备:常见的Linux系统故障及其排查的方法...

    Linux是当前市场上比较常用的.自由开源操作系统,也是云计算运维人员日常工作中的好帮手.不过很多初学云计算的小伙伴面对Linux系统出现的故障束手无策,接下来千锋广州云计算培训小编就给大家分享几个常 ...

  5. layuiajax提交表单控制层代码_究竟怎么用Restful风格编代码必看这篇。(二)

    点击上方"IT咸鱼",星标公众号每天分享技术栈,开发工具等 简单急记几个小点:RequestMapping注解的使用和里面涉及到的参数用途和写法 @RequestMapping(& ...

  6. 总结定时器设计方法_钢结构刚性固定钢柱脚设计方法总结,看完不仅懂操作,还懂了原理...

    一.钢柱柱脚形式的分类 刚性固定柱脚: 1)埋入式柱脚: 2)外包式柱脚: 3)插入式柱脚: 铰接柱脚: 外露式柱脚: 二.埋入式柱脚 2.1.基本概念: 所谓埋入式柱脚是指将钢柱底端直接埋入混凝土基 ...

  7. 大学生web前端期末大作业实例代码 (1500套,建议收藏) HTML+CSS+JS

    文章目录

  8. 力士乐驱动器原理图_力士乐伺服驱动器的常见故障代码和解决方法

    力士乐伺服驱动器的常见故障代码和解决方法:上海仰光维修中心和网友们一起分享下比较常见的故障代码和检修技巧,以供大家参考! 一. 力士乐伺服驱动控制器毛病 伺服驱动由伺服驱动控制器DKC 及程序模块组成 ...

  9. Request对象及其API应用实操

    用户的浏览器往服务器端发送数据的时候,把传送的数据都放在Request对象中进行封装,到服务器端的Servlet代码中就需要使用Request把数据提取出来 文章目录 Request对象 Reques ...

最新文章

  1. python词性标注_文本分类的词性标注
  2. ElementUI的el-table怎样隐藏某一列
  3. cocos2d-x初探学习笔记(12)--图形绘制
  4. 性能优化(6):为什么一定要将css置顶?
  5. OpenCV3学习(11.3)关键点的描述符KeyPoint对象与匹配类DMatch
  6. Netscape 重构软件倒闭了,但我仍坚定地站重写!
  7. 密码输入页面的实现-模仿支付宝
  8. java发送短信的发送报告处理
  9. 谈谈可视化编程 (转)
  10. Objective C 获取当前日期时间方法
  11. 如何使用BitBar将几乎所有信息添加到Mac的菜单栏中
  12. apn(Access Point Name, 接入点名称)简介
  13. /” 和 “\” 有什么区别
  14. c语言中float是什么类型的数据,float是什么数据类型?
  15. python scipy.optimize.minimize多变量多参数优化
  16. 中职计算机技能,中职计算机专业技能竞赛规则
  17. 【操作系统】第十一章死锁与进程通信
  18. 补 2019.11.11-2019.11.17 arts:
  19. 计算机多媒体教室工作总结,多媒体年终工作总结范文精选
  20. ----老家-----

热门文章

  1. SpringMVC4.x源码分析(五):request请求寻址HandlerMethod原理
  2. MFC中利用CFileDialog选择文件并读取文件所遇到的问题和解决方法
  3. 如何重构“箭头型”代码
  4. 使用正则表达式匹配HTML 下各种title标签
  5. 用于制图、写电子邮件、创建条形码控件Aspose.Total
  6. push、pop指令
  7. [转]SQL 约束讲解
  8. ASP.NET无刷新客户端回调
  9. java和内存交互,java内存模型-内存间交互操作
  10. java 中lock,java中lock获取锁的四种方法