乐趣区

关于前端:设计模式关于提高可扩展性方法层面的学习更加从容的应对需求变更

上一节次要学习了进步复用性的几种设计模式,本节学习办法的可扩展性以及怎么更好的扩大办法。

办法是组成程序的根底单元,根底单元的可扩展性是整个程序的可扩展性保障,可扩展性顾名思义是保障代码、程序可能更好的进行扩大。再厉害的程序员都会写 bug,再好的产品经理都会改需要,在遇到需要变更的时候不能总是跟产品硬怼,一个好的程序员须要随时做好改需要的筹备,不要遇到问题就想着砍死产品经理,先思考是不是因为本人的代码可扩展性方面没做好,所以导致改需要才这么难。改代码的时候难免会发现之前的代码思考的不够全面,在这个过程中写第一份代码的时候略微留神一下可扩展性,就能够极大的缩小前面批改代码的难度。

进步可扩展性的目标?

  • 面临需要更改时,不便更改需要
  • 缩小代码批改的难度

什么是好的可扩大?

  • 当产生需要变更的时候,不须要把之前的所有代码推倒重写。好的可扩展性组成的程序就像一块橡皮泥,当你要扭转的时候只须要从新捏一下形态即可。
  • 代码批改不会引起大规模变动。很多人在改代码的时候改了一个 bug 引起十个 bug,按理来说改一个问题只须要加一个解决问题的代码就行了,但因为后期代码设计的不好,导致要批改很多之前的代码能力让这一个性能的代码加上去,这样就会引起大规模批改。
  • 不便退出新模块。从模块的角度讲,比方产品要加一个新模块的时候,咱们整个模块之间的组合就像积木一样,退出一个新模块就像退出一个新积木,拼上去就完事了,这样退出整个模块就十分不便。

一、进步可扩展性的设计模式

1.1. 更好的更改代码的设计模式

这两个模式都是为了让咱们更好的更改代码,它们更像是一种技巧,当咱们的办法须要变更的时候可能让咱们更好的进行变更。

1.1.1. 适配器模式

适配器模式:适配器顾名思义,就是用来做适配的,比方我的电脑只反对 typeC 的耳机,然而我当初只有圆孔的耳机,我要用圆孔的耳机插上电脑来听歌怎么办?第一种计划就是买个 typeC 的耳机,然而买新的太贵了不划算,所以第二种计划就是买个转接头,把圆孔的耳机转接成 typeC 的,这个转接头就是适配器,适配器模式针对的是调用的接口的名字产生了不通用的问题。

目标:通过写一个适配器,来代替间接替换老代码

利用场景:当接口不通用的时候解决接口不通用的问题。比方咱们要调用一个 a 接口,但实际上这个接口名叫 b,两个接口就不适配了,这时候必定不能把 a 改成 b,改了就相当于重写原代码了,通过写适配器,把 a 接口适配到 b 接口就不必去改写老代码了

1.1.2. 装璜者模式

装璜者模式:装璜者模式针对的是办法自身的作用,当一个办法的作用不够用了,须要增加新性能,然而又不能间接去批改之前的办法,应用装璜者模式就能更优雅的扩大咱们的办法。

目标:不重写办法的扩大办法

利用场景:当一个办法须要扩大,但又不好去批改办法。

1.2. 解耦办法与调用的设计模式

1.2.1. 命令模式

命令模式:命令模式针对的是代码设计,命令模式的应用是在设计办法的时候,而不是更改办法的时候,设计之初就要开始思考这个模式

目标:解耦实现和调用,让单方互不烦扰

利用场景:调用的命令充斥不确定性时,能够思考应用命令模式。(性能比拟繁多的时候不要应用,命令层要写很多代码,减少复杂性)

什么是实现?实现就是办法的性能,比方咱们有一个 a 办法,a 办法外面的操作就是实现,而调用就是间接调用 a()

什么是解耦实现和调用?像下面代码示例中定义 a 办法和调用 a 办法相当于:调用 > 办法,而命令模式相当于在调用和办法之间加上一层命令层:调用 > 命令层 > 办法,调用时不必间接去调用办法,而是通过输出命令输出到命令层,而后由命令层来解析命令再去调用具体方法:命令 > 命令层 > 办法

这样做的益处是:

  1. 比照于之前间接调用 > 办法,通过命令层来调用就不须要关怀具体应该调用哪个办法,也不必去理解有哪些办法,只须要关怀输出的命令就好
  2. 比照于之前间接调用 > 办法,之前的形式应用办法的人和办法自身是间接的关系,而在两头退出命令层之后,办法自身和要用办法的人就解耦了,这个时候办法就算产生变动,不用说命令也要跟着变动,只须要在命令层中扭转一下对命令的解析即可,所以说办法的变动就不会影响到命令。同样的,当命令发生变化之后也不会影响到办法,两头都有一层命令层来作为缓冲,咱们能够在命令层做一个调配和调度,这样它们单方产生变动都不会影响到彼此。

咱们写代码其实就相当于命令模式,写好的代码最终在电脑上是怎么用二进制执行的不须要咱们关怀,只须要关怀咱们输出的代码就好,输出的代码就像命令,解析器 V8 引擎相当于命令层,它负责把命令解析成计算机可能执行的二进制语言,办法自身就是计算机去执行的代码。为什么要把写代码变成命令模式?因为编程的方向是多元化的,有可能编写成任何一个成果,能够编写很多成果,所以采纳命令模式十分适合。

二、根本构造

2.1. 适配器模式的根本构造

需要:每次都写 console.log 太麻烦了,我的项目里要用 log 来代替 console.log。

适配器模式应用非常简单,就是在新接口外面调用老接口,适配器模式的利用场景就是在接口不通用的时候做个适配器,解决这个需要只须要在 log 函数中调用 console.log 就能够了。

2.2. 装璜者模式的根本构造

需要:有一个别人写好的 a 模块,外部有一个办法 b,不能批改别人模块的状况下,扩大 b 办法。

装璜者模式三步走:

  1. 封装新办法
  2. 调用老办法
  3. 退出扩大操作

所以咱们新建一个本人的办法,在其外部调用 b 办法,而后退出要扩大的性能,这样就能够在不批改原对象的状况下扩大 b 办法的性能了,代码示例:

2.3. 命令模式的根本构造

代码示例:

下面代码创立一个匿名自执行函数,函数外面有一个 action,它是办法的实现,excute 就是咱们的命令层,通过这个匿名自执行函数拿到的 command 就是命令层,要调用 action 外面的办法时,通过给 command 输出输出命令,这些命令就会在 excute 命令层进行解析来调用 action 外面的实现,使用者不须要关怀具体要调用 action 外面哪个办法。

命令模式有两个因素:

  1. 一个是行为 action
  2. 一个是命令执行层 excute

三、利用示例

3.1. 适配器模式的示例

3.1.1. 框架的变更

需要:目前我的项目中应用 A 框架,A 框架和 jQuery 十分相似,然而 A 框架对进公司的新人很不敌对,新人进入团队还须要学一下这个框架的应用,所以当初要改成 jQuery,这两个框架尽管时候相似,但存在少数几个办法不同。

比方在 jQuery 中 css 的调用是 $.css(),而在 A 框架中是 A.c(),在 jQuery 中绑定事件是 $.on(),而 A 框架中是 A.o(),这就是问题所在,如果这两个框架中的办法名没有任何不一样的话,间接把 A 赋值为 jQuery 就 OK 了,当初存在办法名不一样,间接把 jQuery 赋值给 A 就会导致旧代码中调用 A.css()报错

解决这个问题很多人的解决形式是一个个去找这些办法改掉,过程很显著就是重写老代码,这就是典型的适配器利用场景,咱们只须要写一个适配器,不必改变老代码,让这两个接口名可能适配就好了。依据适配器模式的步骤,在新接口中调用老接口即可,代码示例:

3.1.2. 参数适配

需要:为了防止参数不适配产生问题,很多框架会有一个参数适配操作。

在 JavaScript 中,健壮性十分重要,健壮性的根底保障就是判断参数类型赋予默认值,比方通过 typeof 判断,这种判断对于简略的数据类型是好使的,但如果参数是一个配置对象怎么办?比方 Vue,在 new Vue 时传入的不是一个简略的参数,而是一整个的配置对象,它外面蕴含了很多内容,如 template、data、methods 等等,这些内容外面必定会有一些内容是必填的,比方 template、data,像这样一个对象怎么去保障他人应用的时候传的配置对象外面该必填的都必填呢?

如果应用 typeof 判断只能判断这个配置参数是一个对象,然而配置参数外面的货色有没有必填判断不进去,对于这样的配置对象模式的参数,咱们最好给它做一个参数适配,参数适配就能够了解为适配器模式的变更,当你遇到一些办法它接管的参数是一个配置对象的时候,肯定要养成一个习惯,给这个配置对象做一个参数适配,怎么去保障它外面必传的参数都传了呢?很简略,在函数外面写一个默认的配置对象,在这个默认的配置对象中将必传的属性都写上,比方 name 属性必传、color 属性必传,代码示例:

而后当参数传进来的时候先不急着操作,先做一下适配,循环这个参数,如果传入的参数本身有必传项就用本身的,否则就用默认的代替,这样就保障了必传项起码会有一个默认值,不会因而报错。

当你写的办法要接管的参数是一个配置对象时,就通过这种参数适配的形式去保障必传的参数都有值,这是一个好的习惯,在工作中肯定要放弃。

3.2. 装璜者模式的示例

3.2.1. 扩大已有的事件绑定

需要:我的项目要进行革新,须要给 dom 已有的事件上减少一些操作。

假如你进入一家新公司,接手了前共事的代码,他在 dom 上绑定了很多事件,比方删除按钮绑定了点击事件,点击就进行删除操作,你接手之后产品跟你说感觉之前这种点击就删除没有提醒的形式不太敌对,须要你在点击确定或者删除的同时给出一个提醒,这时候你会怎么做?

很多人会这么做

  • 不去找他之前的代码写在哪了,间接重写整个绑定事件
  • 找到老代码,而后改一下

这两种形式都是错的,如果采纳第一种计划,势必要把它之前的删除性能代码再写一遍,十分麻烦,如果采纳第二种计划,去找老代码这个找的过程也很麻烦,最好的形式就是采纳装璜者模式,装璜者模式是用来干嘛的?就是当你发现一个办法它的原性能不实用、要扩大,但你又不能间接去批改原办法的时候就能够派上用场了。

所以咱们用装璜者模式来做这个事件,思考到要做这个事件的按钮有很多,就不一个个装璜了,采纳工厂模式的思维,间接封装一个装璜工厂,应用时通知我你要装璜哪个 dom,要扩大什么操作就能够了,代码示例:

下面代码中,首先出于健壮性思考,先判断一下 dom 下面有没有绑定事件,有的话再装璜,没有就不论。依据装璜者模式三步走:

  1. 封装新办法,
  2. 调用老办法,
  3. 扩大新性能;

先给 click 事件赋值为新办法,提取出 dom 的老办法,而后在 click 事件的新办法中调用老办法,而后退出咱们要扩大的操作。

在应用的时候比如说要装璜删除按钮,而后要扩大提醒性能,删除之后打印删除胜利

这样既不必去找老代码也不必去从新写整个事件绑定,只须要调用装璜工厂就好了,扩大起来的速度就快多了。

3.2.2. Vue 的数组监听

需要:Vue 中应用 defineProperty 能够监听对象,那么数组监听是怎么实现的?

vue 响应式面临一个窘境,整个 vue2 外面双向绑定是用 defineProperty 来实现的,而这个办法针对的是对象的某个属性,对于数组而言,实现双向绑定比拟艰难。你会发现,在 vue2 外面间接批改数组下标是没方法触发响应式的,所以 vue 从新封装了数组的办法,如 push、replace、shift 等等,通过调用这些办法来触发数组的响应式,怎么让数组的办法可能触发响应式呢?尤雨溪是这么做的:

数组的办法是原生办法,对于设计准则来讲,不可能间接进行批改,所以尤雨溪利用装璜者模式扩大的原生的数组办法性能,使其可能触发响应式。代码示例:

  • 首先把要装璜的办法名放进数组,到时候间接循环数组生成办法就能够了,不必一个个的改变,例如将 push、pop、shift 等进行扩大
  • 在循环开始前先获取一下数组的原型链,因为到时候要装璜的办法全都在原型链上,然而不能间接批改原型链上的办法,所以先拷贝一份
  • 循环数组,拿到咱们要装璜的办法名,而后把咱们要装璜的办法重写于拷贝对象上,装璜者模式三步走,① 重写新办法 ②调用老办法 ③ 扩大新性能;这里的新性能就是调用 dep.notify()来触发 vue 的响应式,这个办法封装在 vue 的源码外面
  • 最初就是将重写的 arrayMethods 对象给到 vue 的 data 下面所有数组原型链上,这样 data 外面的数组原型链上的 push 等办法就有了触发响应式的性能,它也不会影响到原生的数组和办法。

后面两种设计模式都是领导咱们更好的扩大办法,而命令模式是领导咱们更好的设计办法。

3.3. 命令模式的示例

3.3.1. 绘图命令

需要:封装一系列的 canvas 绘图命令

canvas 画图比拟艰难,用过 canvas 的人都晓得,canvas 提供了点、线等 api,绘制图形须要一个个点连贯,很麻烦,为了更不便的应用 canvas 画图,就有大牛对 canvas 进行封装,提供了一些画图形的 api,这样比拟不便。

假如咱们本人封装一个 canvas,提供画圆和画矩形两个 api,如果不必命令模式,代码可能会这样写:

下面代码实用于固定的状态,比方只调用 api 画一个图形或者两个图形,但整个 canvas 的行为是不可能固定的,它可能会须要画 n 个图形,这就很合乎命令模式调用命令充斥不确定性的场景,应用命令模式改写如下:

代码中首先创立命令模式封装,定义好实现层,返回命令层,命令层接管具体的命令,而后跟使用者约定好传什么命令,比方传一个数组,数组中蕴含要画的图形,如果要画两个圆,数组格局为[{type: ‘drawCircle’, radius: 5, num: 2}]。命令层负责解析具体的命令,主动调用实现层外面的办法来实现性能。

而在使用者的角度,使用者只须要调用 canvasCommand 传入相应的命令即可实现性能,他不必去关怀有哪些 api,这样就实现的办法与调用的解耦。其实 webpack 就是一个封装的命令模式,要应用 webpack 的某个性能,不须要去理解 webpack 要调用哪个 api,只须要在配置中配置一下要应用的性能就能够了。

3.3.2. 绘制随机数量图片

需要:做一个画廊,图片数量和排列程序随机

这是一个典型的不确定性利用场景,应用命令模式来封装十分适合,代码如下:

首先创立命令模式的构造

而后约定一下命令,首先格局是一个对象,对象中有一个 imgArr 数组,这个数组寄存图片内容,还有一个排序计划字段,假如用 type 字段,normal 为正序,reverse 为倒序,还有一个 target 属性,这个属性代表创立好的图片要插入到哪个元素外面

命令约定好之后搭建实现层的架子,假如有一个创立 html 构造的 create 办法,一个显示的办法 display。display 办法接管要创立的货色,通过 create 办法创立一个 html 构造并插入到 target 当中

create 办法先不写,先看命令层,命令层接管的是一个对象,对于对象格局的参数咱们最好先做一下 参数适配 避免不必要的谬误;而后调用 action.display 办法传入参数

而后补充 create 办法,这个办法的作用是生成 html 字符串,字符串生成能够采纳相似 vue 的模板引擎来实现,这里做个简略的示例

这种组织形式的益处在于:

  • 对使用者而言不须要理解调用哪个 api,只须要输出命令即可实现性能;
  • 对于代码变更而言,假如命令变了只须要在命令解析层更改一下解析形式就好了,不会影响实现层,同理,如果实现层变了,也不会影响到命令层,只须要更改一下解析就好了。

3.4. 小结

  • 当面临两个新老模块间接口 api 不匹配,能够用适配器模式来转化 api
  • 当老的办法不不便间接批改时,能够通过装璜者模式来扩大性能
  • 应用命令模式解耦实现与具体命令,让实现端和命令端扩大的都更轻松

下一篇:设计模式—对于进步可扩展性(模块层面)的学习(更加从容的应答需要变更)

本文由博客一文多发平台 OpenWrite 公布!

退出移动版