你所须要知道的代码整洁之道

程序是写给人读的,只是偶尔让计算机执行一下。 —— Donald Ervin Knuthgit

每次 review 过往写的代码,总有一种不忍直视的感受。想提升编码能力,故阅读了一些相关书籍及博文,并有所感悟,今将一些读书笔记及我的心得感悟梳理出来。抛转引玉,但愿这砖能抛得起来。程序员

大纲

  • 坏味道的代码
  • 圈复杂度
  • 重构
  • 代码整洁之道
  • 编码原则 & 设计模式
  • 总结
  • 参考

坏味道的代码

开始阅读以前,你们能够快速思考一下,你们脑海里的好代码和坏代码都是怎么样的“形象”呢?github

若是看到这一段代码,如何评价呢?算法

if (a && d || b && c && !d || (!a || !b) && c) {
  // ...
} else {
  // ...
}
复制代码

上面这段代码,尽管是特地为举例而写的,要是真实遇到这种代码,想必你们都“一言难尽”吧。你们多多少少都有一些坏味道的代码的“印象”,坏味道的代码总有一些共性:编程

  • Duplicated Code(重复代码)
  • Long Method(过长函数)
  • Large Class (过大的类)
  • Long Parameter List(过长参数列)
  • Temporary Field(使人迷惑的暂时字段)
  • Shotgun Surgery(霰弹式修改):一种变化引起多个类相应修改
  • ......

那坏味道的代码是怎样造成的呢?设计模式

  • 上一个写这段代码的程序员经验、水平不足,或写代码时不够用心;
  • 产品经理提出的奇葩需求致使写了不少hack代码;
  • 某一个模块业务太复杂,需求变动的次数太多,经手的程序员太多。
  • ......

对坏味道的代码有一个大概的了解后,或许读者心中有一个疑问:代码的好坏有没有一些量化的标准去评判呢?答案是确定的。bash

接下来,经过了解圈复杂度去衡量咱们写的代码。然而当代码的坏味道已经“弥漫”处处都是了,这时咱们应该了解一下重构。代码到了咱们手里,不能继续“发散”坏味道,这时应该了解如何编写 clean code。此外,咱们还应该掌握一些编码原则及设计模式,这样才能作到有的放矢。markdown

圈复杂度

圈复杂度(Cyclomatic complexity,简写CC)也称为条件复杂度,是一种代码复杂度的衡量标准。由托马斯·J·麦凯布(Thomas J. McCabe, Sr.)于1976年提出,用来表示程序的复杂度。架构

圈复杂度能够用来衡量一个模块断定结构的复杂程度,数量上表现为独立现行路径条数,也可理解为覆盖全部的可能状况最少使用的测试用例数。app

断定方法

圈复杂度能够经过程序控制流图计算,公式为:

V(G) = e + 2 - n

  • e : 控制流图中边的数量
  • n : 控制流图中节点的数量

有一个简单的计算方法:圈复杂度实际上就是等于断定节点的数量再加上1。

注:if elseswitch casefor循环三元运算符||&&等,都属于一个断定节点。

衡量标准

代码复杂度低,代码不必定好,但代码复杂度高,代码必定很差。

圈复杂度 代码情况 可测性 维护成本
1 - 10 清晰、结构化
10 - 20 复杂
20 - 30 很是复杂
>30 不可读 不可测 很是高

圈复杂度检测方法

ESLint 规则

ESLint 提供了检测代码圈复杂度的 rules。开启 rules 中的 complexity 规则,并将圈复杂度大于 0 的代码的 rule severity 设置为 warn 或 error 。

rules: {
    complexity: [
        'warn',
        { max: 0 }
    ]
}
复制代码

CLIEngine

借助 ESLint 的 CLIEngine ,在本地使用自定义的 ESLint 规则扫描代码,并获取扫描结果输出。

下降代码的圈复杂度

不少状况下,下降圈复杂度就能提升代码的可读性了。针对圈复杂度,结合例子给出一些改善的建议:

1. 抽象配置

经过抽象配置将复杂的逻辑判断进行简化。

before:

// ...
if (type === '扫描') {
    scan(args)
} else if (type === '删除') {
    delete(args)
} else if (type === '设置') {
    set(args)
} else {
    // ...
}
复制代码

after:

const ACTION_TYPE = {
    扫描: scan,
    删除: delete,
    设置: set
}
ACTION_TYPE[type](args)
复制代码

2. 提炼函数

将代码中的逻辑进行抽象提炼成单独的函数,有利于下降代码复杂度和下降维护成本。尤为是当一个函数的代码很长,读起来很费力的时候,就应该思考可否提炼成多个函数。

before:

function example(val) {
    if (val > MAX_VAL) {
        val = MAX_VAL
    }
    for (let i = 0; i < val; i++) {
        doSomething(i)
    }
    // ...
}
复制代码

after:

function setMaxVal(val) {
    return val > MAX_VAL ? MAX_VAL : val
}

function getCircleArea(val) {
    for (let i = 0; i < val; i++) {
        doSomething(i)
    }
}
function example(val) {
    return getCircleArea(setMaxVal(val))
}
复制代码

3. 逆向条件简化条件判断

某些复杂的条件判断可能逆向思考后会变的更简单,还能减小嵌套。

before:

function checkAuth(user){
    if (user.auth) {
        if (user.name === 'admin') {
            // ...
        } else if (user.name === 'root') {
            // ...
        }
    }
}
复制代码

after:

function checkAuth(user){
    if (!user.auth) return
    if (user.name === 'admin') {
        // ...
    } else if (user.name === 'root') {
        // ...
     }
}
复制代码

4. 合并条件简化条件判断

将冗余的条件合并,而后再进行判断。

before:

if (fruit === 'apple') {
    return true
} else if (fruit === 'cherry') {
    return true
} else if (fruit === 'peach') {
    return true
} else {
    return true
}
复制代码

after:

const redFruits = ['apple', 'cherry', 'peach']
if (redFruits.includes(fruit) {
    return true
}
复制代码

5. 提取条件简化条件判断

对复杂难懂的条件进行提取并语义化。

before:

if ((age < 20 && gender === '女') || (age > 60 && gender === '男')) {
    // ...
} else {
    // ...
}
复制代码

after:

function isYoungGirl(age, gender) {
    return (age < 20 && gender === '女'
}
function isOldMan(age, gender) {
    return age > 60 && gender === '男'
}
if (isYoungGirl(age, gender) || isOldMan(age, gender)) {
    // ...
} else {
    // ...
}
复制代码

后文有简化条件表达式更全面的总结。

重构

重构一词有名词和动词上的理解。名词:

对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提升其可理解性,下降其修改为本。

动词:

使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。

为什么重构

若是遇到如下的状况,可能就要思考是否须要重构了:

  • 重复的代码太多
  • 代码的结构混乱
  • 程序没有拓展性
  • 对象结构强耦合
  • 部分模块性能低

为什么重构,不外乎如下几点:

  • 重构改进软件设计
  • 重构使软件更容易理解
  • 重构帮助找到 bug
  • 重构提升编程速度

重构的类型:

  • 对现有项目进行代码级别的重构;
  • 对现有的业务进行软件架构的升级和系统的升级。

本文讨论的内容只涉及第一点,仅限代码级别的重构。

重构时机

第一次作某件事时只管去作;第二次作相似的事会产生反感,但不管如何仍是能够去作;第三次再作相似的事,你就应该重构。

  • 添加功能时:当添加新功能时,若是发现某段代码改起来特别困难,拓展功能特别不灵活,就要重构这部分代码使添加新特性和功能变得更容易;
  • 修补错误时:在你改 bug 或查找定位问题时,发现本身之前写的代码或者别人的代码设计上有缺陷(如扩展性不灵活),或健壮性考虑得不够周全(如漏掉一些该处理的异常),致使程序频繁出现问题,那么此时就是一个比较好的重构时机;
  • 复审代码时:团队进行 Code Review 的时候,也是一个进行重构的合适时机。

代码整洁之道

关键思想

  • 代码应当易于理解;
  • 代码的写法应当使别人理解它所需的时间最小化。

代码风格

关键思想:一致的风格比“正确”的风格更重要。

原则:

  • 使用一致的布局
  • 让类似的代码看上去类似
  • 把相关的代码行分组,造成代码块

注释

注释的目的是尽可能帮助读者了解得和做者同样多。所以注释应当有很高的信息/空间率。

不须要注释

  • 不要为了注释而注释
  • 不要给很差的名字加注释(应该把名字起好)
  • 不要为那些从代码自己就能快速推断的事实写注释

须要写注释

  • 对于为何代码写成这样而不是那样的内在理由(指导性批注)
  • 常量背后的故事,为何是这个值
  • 给读者意料以外的行为加上注释
  • 在文件/类的级别上使用“全局观”注释来解释全部的部分是如何一块儿工做的
  • 用注释来总结代码块,使读者不致迷失在细节中
  • 尽可能精确地描述函数的行为
  • 声明代码的高层次意图,而非明显的细节
  • 在注释中使用输入/输出例子进行说明
  • 代码中的缺陷,使用标记
标记 一般的意义
TODO: 还没处理的事情
FIXME: 已知的没法运行的代码
HACK: 对一个问题不得不采用的比价粗糙的解决方案

命名

关键思想:把信息装入名字中。

良好的命名是一种以“低代价”取得代码高可读性的途径。

选择专业名词

“把信息装入名字中”包括要选择很是专业的词,而且避免使用“空洞”的词。

单词 更多选择
send deliver, despatch, announce, distribute, route
find search, extract, locate, recover
start launch, create, begin, open
make create, set up, build, generate, compose, add, new

避免像tmp和retval这样泛泛的名字

  • retval这个名字没有包含不少信息
  • tmp 只应用于短时间存在且临时性为其主要存在因素的变量

用具体的名字代替抽象的名字

在给变量、函数或者其余元素命名时,要把它描述得更具体而不是更抽象。

为名字附带更多信息

若是关于一个变量有什么重要事情的读者必须知道,那么是值得把额外的“词”添加到名字中的。

名字的长度

  • 在小的做用域里可使用短的名字
  • 为做用域大的名字采用更长的名字
  • 丢掉没用的词

不会被误解的名字

  • 用min和max来表示极限
  • 用first和last来表示包含的范围
  • 用begin和end来表示排除范围
  • 给布尔值命名:is,has,can,should

语义相反的词汇要成对出现

add remove
create destory
insert delete
get set
increment decrement
show hide
start stop

其余命名小建议

  • 计算限定符做为前缀或后缀(Avg、Sum、Total、Min、Max)
  • 变量名要能准确地表示事物的含义
  • 用动名词命名函数名
  • 变量名的缩写,尽可能避免不常见的缩写

简化条件表达式

分解条件表达式

有一个复杂的条件(if-then-else)语句,从if、then、else三个段落中分别提炼出独立函数。根据每一个小块代码的用途,为分解而获得的新函数命名,并将原函数中对应的代码改成调用新建函数,从而更清楚地表达本身的意图。对于条件逻辑,能够突出条件逻辑,更清楚地代表每一个分支的做用,而且突出每一个分支的缘由。

合并条件表达式

有一系列条件测试,都获得相同结果。将这些测试合并为一个条件表达式,并将这个条件表达式提炼成为一个独立函数。

  • 肯定这些条件语句都没有反作用;
  • 使用适当的逻辑操做符,将一系列相关条件表达式合并为一个;
  • 对合并后的条件表达式实施提炼函数。

合并重复的条件片断

在条件表达式的每一个分支上有着相同的一段代码,将这段重复代码搬移到条件表达式以外。

以卫语句取代嵌套条件表达式

函数中的条件逻辑令人难以看清正常的执行路径。使用卫语句表现全部特殊状况。

若是某个条件极其罕见,就应该单独检查该条件,并在该条件为真时马上从函数中返回。这样的单独检查经常被称为“卫语句”(guard clauses)。

经常能够将条件表达式反转,从而实以卫语句取代嵌套条件表达式,写成更加“线性”的代码来避免深嵌套。

变量与可读性

变量存在的问题:

  • 变量越多,就越难所有跟踪它们的动向
  • 变量的做用域越大,就须要跟踪它的动向越久
  • 变量改变得越频繁,就越难以跟踪它的当前值

内联临时变量

若是有一个临时变量,只是被简单表达式赋值一次,而将全部对该变量的引用动做,替换为对它赋值的那个表达式自身。

以查询取代临时变量

以一个临时变量保存某一表达式的运算结果,将这个表达式提炼到一个独立函数中。将这个临时变量的全部引用点替换为对新函数的调用。此后,新函数就可被其余函数使用。

总结变量

接上条,若是该表达式比较复杂,建议经过一个总结变量名来代替一大块代码,这个名字会更容易管理和思考。

引入解释性变量

将复杂表达式(或其中一部分)的结果放进一个临时变量,以此变量名称来解释表达式用途。

在条件逻辑中,引入解释性变量特别有价值:能够将每一个条件子句提炼出来,以一个良好命名的临时变量来解释对应条件子句的意义。使用这项重构的另外一种状况是,在较长算法中,能够运用临时变量来解释每一步运算的意义。

好处:

  • 把巨大的表达式拆分红小段
  • 经过用简单的名字描述子表达式来让代码文档化
  • 帮助读者识别代码中的主要概念

分解临时变量

程序有某个临时变量被赋值超过一次,它既不是循环变量,也不是用于收集计算结果。针对每次赋值,创造一个独立、对应的临时变量。

临时变量有各类不一样用途:

  • 循环变量
  • 结果收集变量(经过整个函数的运算,将构成的某个值收集起来)

若是临时变量承担多个责任,它就应该被替换(分解)为多个临时变量,每一个变量只承担一个责任。

以字面常量取代 Magic Number

有一个字面值,带有特别含义。创造一个常量,根据其意义为它命名,并将上述的字面数值替换为这个常量。

减小控制流变量

let done = false;

while (condition && !done) {
    if (...) {
        done = true;
        continue;
    }
}
复制代码

像done这样的变量,称为“控制流变量”。它们惟一的目的就是控制程序的执行,没有包含任何程序的数据。控制流变量一般能够经过更好地运用结构化编程而消除。

while (condition) {
    if (...) {
        break;
    }
}
复制代码

若是有多个嵌套循环,一个简单的break不够用,一般解决方案包括把代码挪到一个新函数中。

从新组织函数

提炼函数

当一个过长的函数或者一段须要注释才能让人理解用途的代码,能够将这段代码放进一个独立函数中。

  • 函数的粒度小,被复用的机会就很大;
  • 函数的粒度小,覆写也会更容易些。

一个函数过长才合适?长度不是问题,关键在于函数名称和函数本体之间的语义距离。

将查询函数和修改函数分离

某个函数既返回对象状态值,又修改对象状态。创建两个不一样的函数,其中一个负责查询,另外一个负责修改。

以明确函数取代参数

有一个函数,其中彻底取决于参数值而采起不一样行为。针对该参数的每个可能值,创建一个独立函数。

引入参数对象

某些参数老是很天然地同时出现,以一个对象取代这些参数。

从函数中提早返回

能够经过立刻处理“特殊状况”,并从函数中提早返回。

拆分巨大的语句

若是有很难读的代码,尝试把它所作的全部任务列出来。其中一些任务能够很容易地变成单独的函数(或类)。其余的能够简单地成为一个函数中的逻辑“段落”。

其余建议及思路

减小循环内的嵌套

在循环中,与提前返回相似的技术是continue。与if...return;在函数中所扮演的保护语句同样,if...continue;语句是循环中的保护语句。(注意:JavaScript 中 forEach 的特殊性)

德·摩根定律

对于一个布尔表达式,有两种等价写法:

  • not (a or b or c) <=> (not a) and (not b) and (not c)
  • not (a and b and c) <=> (not a) or (not b) or (not c)

可使用这些法则让布尔表达式更具备可读性。例如:

// before
if (!(file_exitsts && !is_protected)) Error("sorry, could not read file.")

// after
if (!file_exists || is_protected) Error("sorry, could not read file.")
复制代码

使用相关定律能优化开始举例的那段代码:

// before
if (a && d || b && c && !d || (!a || !b) && c) {
  // ...
} else {
  // ...
}

// after
if (a && d || c) {
  // ...
} else {
  // ...
}
复制代码

具体简化过程及涉及相关定律能够参考这篇推文:你写这样的代码,不怕同事打你嘛?

从新组织代码

所谓工程学就是关于把大问题拆分红小问题再把这些问题的解决方案放回一块儿。

把这条原则应用于代码会使代码更健壮而且更容易读。

积极地发现并抽取不相关的子逻辑,是指:

  • 看看某个函数或代码块,问问本身:这段代码高层次的目标是什么?
  • 对于每一行代码,问一下:它是直接为了目标而工做吗?
  • 若是足够的行数在解决不相关的子问题,抽取代码到独立的函数中。

把想法变成代码

若是你不能把一件事解释给你祖母听的话说明你还没真正理解它。 --阿尔伯特·爱因斯坦

步骤以下:

  1. 像对着一个同事同样用天然语言描述代码要作什么
  2. 注意描述中所用的关键词和短语
  3. 写出与描述所匹配的代码

编码原则 & 设计模式

有必要熟知前人总结的一些经典的编码原则及涉及模式,以此来改善咱们既有的编码习惯,所谓“站在巨人肩上编程”。

SOLID原则

SOLID 是面向对象设计(OOD)的五大基本原则的首字母缩写组合,由俗称“鲍勃大叔”的Robert C.Martin在《敏捷软件开发:原则、模式与实践》一书中提出来。

  • S(Single Responsibility Principle):单一职责原则,简称SRP
  • O(Open Close Principle):开放封闭原则,简称OCP
  • L(Liskov Substitution Principle):里氏替换原则,简称LSP
  • I(Interface SegregationPrinciple):接口隔离原则,简称ISP
  • D(Dependence Inversion Principle):依赖倒置原则,简称DIP

单一职责原则

A class should have only one reason to change.

一个类应该有且仅有一个缘由引发它的变动。

通俗来说:一个类只负责一项功能或一类类似的功能。固然这个“一”并非绝对的,应该理解为一个类只负责尽量独立的一项功能,尽量少的职责。

优势:

  • 功能单一,职责清晰。
  • 加强可读性,方便维护。

缺点:

  • 拆分得太详细,类的数量会急剧增长。
  • 职责的度量没有统一的标准,须要根据项目实现状况而定。

这条定律一样适用于组织函数时的编码原则。

开放封闭原则

Software entities (classes,modules,functions,etc.)should be openfor extension,but closed for modification.

软件实体(如类、模块、函数等)应该对拓展开放,对修改封闭。

在一个软件产品的生命周期内,不可避免会有一些业务和需求的变化,咱们在设计代码的时候应该尽量地考虑这些变化。在增长一个功能时,应当尽量地不去改动已有的代码;当修改一个模块时不该该影响到其余模块。

const makeSound = function( animal ) { 
    animal.sound(); 
};

const Duck = function(){}; 
Duck.prototype.sound = function(){ 
    console.log( '嘎嘎嘎' ); 
};

const Chicken = function(){}; 
Chicken.prototype.sound = function(){ 
    console.log( '咯咯咯' ); 
}; 

makeSound( new Duck() ); // 嘎嘎嘎
makeSound( new Chicken() ); // 咯咯咯
复制代码

里氏替换原则

Functions that use pointers to base classes must be able to useobjects of derived classes without knowing it.

全部能引用基类的地方必须能透明地使用其子类的对象。

只要父类能出现的地方子类就能出现(就能够用子类来替换它)。反之,子类能出现的地方父类不必定能出现(子类拥有父类的全部属性和行为,但子类拓展了更多的功能)。

接口隔离原则

Clients should not be forced to depend upon interfaces that they don't use.Instead of one fat interface many small interfaces arepreferred based on groups of methods,each one serving onesubmodule.

客户端不该该依赖它不须要的接口。用多个细粒度的接口来替代由多个方法组成的复杂接口,每个接口服务于一个子模块。

接口尽可能小,可是要有限度。当发现一个接口过于臃肿时,就要对这个接口进行适当的拆分。可是若是接口太小,则会形成接口数量过多,使设计复杂化。

依赖倒置原则

High level modules should not depend on low level modules; bothshould depend on abstractions.Abstractions should not depend ondetails.Details should depend upon abstractions.

高层模块不该该依赖低层模块,两者都该依赖其抽象。抽象不该该依赖细节,细节应该依赖抽象。

把具备相同特征或类似功能的类,抽象成接口或抽象类,让具体的实现类继承这个抽象类(或实现对应的接口)。抽象类(接口)负责定义统一的方法,实现类负责具体功能的实现。

是否必定要遵循这些设计原则

  • 软件设计是一个逐步优化的过程
  • 不是必定要遵循这些设计原则

没有这么充足的时间遵循这些原则去设计,或遵循这些原则设计的实现成本太大。在受现实条件所限不能遵循五大原则来设计时,咱们还能够遵循下面这些更为简单、实用的原则。

简单、实用的原则

LoD原则(Law of Demeter)

Each unit should have only limited knowledge about other units: onlyunits "closely"related to the current unit.Only talk to your immediatefriends,don't talk to strangers.

每个逻辑单元应该对其余逻辑单元有最少的了解:也就是说只亲近当前的对象。只和直接(亲近)的朋友说话,不和陌生人说话。

这一原则又称为迪米特法则,简单地说就是:一个类对本身依赖的类知道的越少越好,这个类只须要和直接的对象进行交互,而不用在意这个对象的内部组成结构。

例如,类A中有类B的对象,类B中有类C的对象,调用方有一个类A的对象a,这时若是要访问C对象的属性,不要采用相似下面的写法:

a.getB().getC().getProperties()
复制代码

而应该是:

a.getProperties()
复制代码

KISS原则(Keep It Simple and Stupid)

Keep It Simple and Stupid.

保持简单和愚蠢。

  • “简单”就是要让你的程序能简单、快速地被实现;
  • “愚蠢”是说你的设计要简单到任何人都能理解,即简单就是美!

DRY原则(Don't Repeat Yourself)

DRY原则(Don't Repeat Yourself)

不要重复本身。

不要重复你的代码,即屡次遇到一样的问题,应该抽象出一个共同的解决方法,不要重复开发一样的功能。也就是要尽量地提升代码的复用率。

要遵循DRY原则,实现的方式很是多:

  • 函数级别的封装:把一些常用的、重复出现的功能封装成一个通用的函数。
  • 类级别的抽象:把具备类似功能或行为的类进行抽象,抽象出一个基类,并把这几个类都有的方法提到基类去实现。
  • 泛型设计:Java 中可以使用泛型,以实现通用功能类对多种数据类型的支持;C++中可使用类模板的方式,或宏定义的方式;Python中可使用装饰器来消除冗余的代码。

DRY原则在单人开发时比较容易遵照和实现,但在团队开发时不太容易作好,特别是对于大团队的项目,关键仍是团队内的沟通。

YAGNI原则(You Aren't Gonna Need It)

You aren't gonna need it,don't implement something until it isnecessary.

你不必那么着急,不要给你的类实现过多的功能,直到你须要它的时候再去实现。

  • 只考虑和设计必需的功能,避免过分设计。
  • 只实现目前须要的功能,在之后须要更多功能时,能够再进行添加。
  • 如无必要,勿增长复杂性。

Rule Of Three原则

Rule of three 称为“三次法则”,指的是当某个功能第三次出现时,再进行抽象化,即事不过三,三则重构。

  • 第一次实现一个功能时,就尽管大胆去作;
  • 第二次作相似的功能设计时会产生反感,可是还得去作;
  • 第三次还要实现相似的功能作一样的事情时,就应该去审视是否有必要作这些重复劳动了,这个时候就应该重构你的代码了,即把重复或类似功能的代码进行抽象,封装成一个通用的模块或接口。

CQS原则(Command-Query Separation)

  • 查询(Query):当一个方法返回一个值来回应一个问题的时候,它就具备查询的性质;
  • 命令(Command):当一个方法要改变对象的状态的时候,它就具备命令的性质。

保证方法的行为严格的是命令或者查询,这样查询方法不会改变对象的状态,没有反作用;而会改变对象的状态的方法不可能有返回值。

设计模式

设计模式的开山鼻祖 GoF 在《设计模式:可复用面向对象软件的基础》一书中提出的23种经典设计模式被分红了三类,分别是建立型模式、结构型模式和行为型模式。

在面向对象软件设计过程当中针对特定问题的简洁而优雅的解决方案。

经常使用的设计模式有:策略模式、发布—订阅模式、职责链模式等。

好比策略模式使用的场景:

策略模式:定义一系列的算法,把它们一个个封装起来,而且使它们能够相互替换。

if (account === null || account === '') {
    alert('手机号不能为空');
    return false;
}
if (pwd === null || pwd === '') {
    alert('密码不能为空');
    return false;
}
if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(account)) {
    alert('手机号格式错误');
    return false;
}
if(pwd.length<6){
    alert('密码不能小于六位');
    return false;
}
复制代码

使用策略模式:

const strategies = {
    isNonEmpty: function (value, errorMsg) {
        if (value === '' || value === null) {
            return errorMsg;
        }
    },
    isMobile: function (value, errorMsg) { // 手机号码格式
        if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(value)) {
            return errorMsg;
        }
    },
    minLength: function (value, length, errorMsg) {
        if (value.length < length) {
            return errorMsg;
        }
    }
};

const accountIsMobile = strategies.isMobile(account,'手机号格式错误');
const pwdMinLength = strategies.minLength(pwd,8,'密码不能小于8位');
const errorMsg = accountIsMobile || pwdMinLength; 
if (errorMsg) {
    alert(errorMsg);
    return false;
}
复制代码

又好比,发布—订阅模式具备的特色:

  • 时间上的解耦
  • 对象之间的解耦

既能够用在异步编程中,也能够帮助咱们完成更松耦合的代码编写。

若是你们须要了解设计模式更多知识,建议另外找资料学习。

总结

宋代禅宗大师青原行思提出参禅的三重境界:

参禅之初,看山是山,看水是水;禅有悟时,看山不是山,看水不是水;禅中彻悟,看山还是山,看水还是水。

同理,编程一样存在境界:编程的一重境界是照葫芦画瓢,二重境界是能够灵活运用,三重境界则是心中无模式。惟有多实践,多感悟,方能突破一重又一重的境界。

最后,愿你们终将能写出本身再也不讨厌的代码。

最后

真的是最后了,有空会补上上述的示例代码,欢迎你们 Star & PR 呀:你所须要知道的代码整洁之道

参考

  • 《代码整洁之道》
  • 《编写可读代码的艺术》
  • 《重构-改善既有代码的设计》
  • 《JavaScript设计模式与开发实践》
  • 《人人都懂设计模式:从生活中领悟设计模式:Python实现》
相关文章
相关标签/搜索