代码质量管理——如何写出优雅的代码

做为一个刚写代码不久的小菜鸟,工做的半年多让我愈加意识到提升代码质量的重要性。从前只会关注实现功能,慢慢的开始关注性能,现阶段则发现其实还有不少细节也是(如可读性、易用性、可维护性、一致性)提升代码质量的关键。“实现功能”跟“优雅地实现功能”是两码事。javascript

注:大部份内容概括自网络,将多篇文章的观点汇总加工了一下,也融合了一些我的的看法。css

原则

  • 单一职责原则html

  • 易用性原则java

  • 可读性原则ajax

  • 复杂性守恒原则:不管你怎么写代码,复杂性都是不会消失的编程

    注:若是逻辑很复杂,那么代码看起来就应该是复杂的。若是逻辑很简单,代码看起来就应该是简单的。segmentfault

单一职责原则

面向对象五大设计模式基本原则之一。即一部分代码只应该用于某一个特定功能,不该与其余功能耦合在一块儿windows

假设你的一个function同时实现了功能a和功能b,以后需求变动,你须要修改功能a,可是由于这两个功能都在一个function里,你就不得再也不去确认是否会影响到功能b。这就形成了没必要要的成本。设计模式

以下我总结了三个拆分代码的原则:api

“and”原则

当你为你的方法命名时不得不加上“and”时,就该考虑考虑是否是要把这个方法拆分一下了。

“100”原则

当你的一个function超过一百行时,必定要进行拆分了。

注:这里的100可能有点多,只是对我我的而言,100算是个人极限,总之就是绝对不要将一个函数写的太长。

命令、查询拆分原则

咱们开发中大部分操做能够总结为“命令”和“查询”,如写cookie、修改data、发送post请求均可以叫“命令”,而读取cookie、ajax获取数据则认为是“查询”操做。

函数式编程中讲究“数据不可变”,即:

只有纯的没有反作用的函数,才是合格的函数。

反作用:指当调用函数时,除了返回函数值以外,还对主调用函数产生附加的影响。例如修改全局变量(函数外的变量)或修改参数。

好处是使得开发更加简单,可回溯,测试友好,减小了任何可能的反作用。

将“命令”与“查询”拆分实际上就是函数式编程思想的部分体现,参考以下代码:

function getFirstName() {
    var firstName = document.querySelector("#firstName").value;
    firstName = firstName.toLowerCase();
    setCookie("firstName", firstName);
    if (firstName === null) {
        return "";
    }
    return firstName;
}
 
var activeFirstName = getFirstName();
复制代码

经过名字来看,该方法是用于获取first name的,但实际上它还设置了cookie,这是咱们没有预料的。对于一个“查询”方法,它不该该有任何修改方法外变量的行为,即“反作用”。更好的写法以下:

function getFirstName() {
    var firstName = document.querySelector("#firstName").value
    if (firstName === null) {
        return "";
    }
    return firstName;
}
 
setCookie("firstName", getFirstName().toLowerCase());
复制代码

一目了然,getFirstName只返回firstName,而设置cookie操做在它以外进行。

易用性原则

简单

这里的简单,主要归结为function的一些设计原则,有以下几点:调用简单、易理解、减小记忆成本、参数处理。

以下,仅仅想实现修改dom颜色、宽度等属性,原生代码以下:

document.querySelector('#id').style.color = 'red'
document.querySelector('#id').style.width = '123px'
document.querySelector('#id').style.height = '456px'
复制代码

而封装事后:

function a(selector, color, width, height) {
  document.querySelector(selector).style.color = color
  document.querySelector(selector).style.width = width
  document.querySelector(selector).style.height = height
}
a('#a', 'red')
复制代码

瞬间变得简单可用了 ~

但该方法还存在一个问题,那就是命名太抽象了。。。除了开发者本身之外不可能有人在不看源码的状况下一眼看出这个方法a是干吗的。那么咱再把这个方法名改写得更易理解一点:

function letSomeElementChange(selector, color, width, height) {
  document.querySelector(selector).style.color = color
  document.querySelector(selector).style.width = width
  document.querySelector(selector).style.height = height
}
复制代码

这样咱们就能一目了然该方法的做用 ~ 不过仍有可优化的地方。这么长的方法名谁记得住,要减小记忆成本啊,再改个名:

function setElement(selector, color, width, height) {
  document.querySelector(selector).style.color = color
  document.querySelector(selector).style.width = width
  document.querySelector(selector).style.height = height
}
复制代码

OK,目前这个方法已经知足它的职责而且很好用了,但还以为怪怪的。这一坨参数太碍眼。。。

function setElement(selector, opt) {
  const { color, width, height } = opt
  color && document.querySelector(selector).style.color = color
  width && document.querySelector(selector).style.width = width
  height && document.querySelector(selector).style.height = height
}
复制代码

把多个参数合并一下,并在内部作兼容处理,这个方法便易用多了。即便不传第二个参数也不会有任何反作用。

一致性

假若有这样一个方法,获取歌曲列表,并将其设置到div的innerText中:

function getSongs() {
    return $.get('/songs).then((response) {
        div.innerText = response.songs
  })
}
复制代码

这就违背了方法的表里一致性,也违背了上文的单一职责原则命令、查询拆分原则,由于它不只获取了歌单,同时还修改了innerText,要让其更合理:

  • 要么换个名字
  • 要么拆分为两个方法

低耦合

耦合是衡量一个程序单元对其余程序单元的依赖程度。耦合(或高耦合)是应该极力避免的。若是你发现本身正在复制和粘贴代码并进行小的更改,或者重写代码,由于其余地方发生了更改,这就是高耦合的体现。

耦合会严重影响代码的复用性及可扩展性,让后人维护时不得不修改甚至重写这部分代码,不只浪费时间还会致使仓储中又多出一块相似的代码,很容易让人迷惑。

同时,修改耦合度高的代码时常常会牵一发而动全身,若是修改时没有理清这些耦合关系,那么带来的后果可能会是灾难性的,特别是对于需求变化较多以及多人协做开发维护的项目,修改一个地方会引发原本已经运行稳定的模块错误,严重时会致使恶性循环,问题永远改不完,开发和测试都在各类问题之间奔波劳累,最后致使项目延期,用户满意度下降,成本也增长了,这对用户和开发商影响都是很恶劣的,各类风险也就不言而喻了。

高内聚

不该该将没有任何联系的东西堆到一块儿。

内聚是一个类中变量与方法链接强度的尺度。高内聚是值得要的,由于它意味着类能够更好地执行一项工做。低内聚是很差的,由于它代表类中的元素之间不多相关。每一个方法也应该高内聚,大多数的方法只执行一个功能,不要在方法中添加‘额外’的指令,这样会致使方法执行更多的函数,同时也违反了上文的单一职责原则

低内聚的体现:若是属性没有被类中的多个方法使用,这多是低内聚的标志。一样,若是方法在几种不一样的状况下不能被重用,或者若是一个方法根本不被使用,这也多是低内聚的一个标志。

高内聚有助于缓解高耦合,高耦合是须要高内聚的标志。可是,若是两个问题同时存在,应当选择内聚的方式。对于开发者来讲,高内聚一般比低耦合更有帮助,尽管二者一般能够一块儿完成。

错误处理

  • 可预见的错误:诸如ajax回调、函数参数,这类问题很好解决,只需在开发时多考虑一步,对各类极端状况作好兼容便可。
  • 不可预见的错误:相似兼容性问题,这类问题没法在开发时准确预见的错误,能够准备好抛错,console.error/log/warn,最后你还能够为本身的程序留些后路: try...catch。

可读性原则

命名

命名应该保证别人经过名称一眼就能知道这个变量保存的是什么,或者这个方法是用来作什么的。

  • 普通变量、属性用名词以下:

    var person = {
        name: 'Frank'
    }
    var student = {
        grade: 3,
        class: 2
    }
    复制代码
  • bool变量、属性用(形容词)或者(be动词)或者(情态动词)或者(hasX),以下:

    var person = {
        dead: false, // 若是是形容词,前面就不必加 is,好比isDead 就很废话
        canSpeak: true, //情态动词有 can、should、will、need 等,情态动词后面接动词
        isVip: true, // be 动词有 is、was 等,后面通常接名词
        hasChildren: true, // has 加名词
    }
    复制代码
  • 普通函数、方法用(动词)开头:

    var person = {
        run(){}, // 不及物动词
        drinkWater(){}, // 及物动词
        eat(foo){}, // 及物动词加参数(参数是名词)
    }
    复制代码
  • 回调、钩子函数:

    var person = {
        beforeDie(){},
        afterDie(){},
        // 或者
        willDie(){}
        dead(){} // 这里跟 bool 冲突,你只要不一样时暴露 bool dead 和函数 dead 就行,怕冲突就用上面的 afterDie
    }
    button.addEventListener('click', onButtonClick)
    var component = {
        beforeCreate(){},
        created(){},
        beforeMount(){}
    }
    复制代码
  • 命名一致性

    • 顺序一致性:好比 updateContainerWidth 和 updateHeightOfContainer 的顺序就使人很别扭
    • 时间一致性:有可能随着代码的变迁,一个变量的含义已经不一样于它一开始的含义了,这个时候你须要及时改掉这个变量的名字。 这一条是最难作到的,由于写代码容易,改代码难。若是这个代码组织得很差,极可能会出现牵一发而动全身的状况(如全局变量就很难改)

注释

不须要多花哨,只要把做用、用法描述清楚便可。方法的标准注释应该以下:

/** * [function_name description] * @param {[type]} argument [description] * @return {[type]} [description] */
function function_name(argument) {
  // body...
}
复制代码

将方法的参数与返回值都写清楚,我目前用的IDE是sublime,使用Docblockr插件能够自动生成格式化注释,很方便。

Bad Smell

项目中咱们常常可以遇这类代码,它们仍可用,可是很“臭”,国外管这类代码有一个统称,即“bad smell”。以下这类代码能够说是很“臭”了:

  • 表里不一的代码
  • 过期的注释
  • 逻辑很简单,可是看起来很复杂的代码
  • 重复的代码
  • 类似的代码
  • 老是一块儿出现的代码
  • 未使用的依赖
  • 不一样风格的代码

样式规范

  1. 正确命名:class必须用“-”写法,不要用驼峰和下划线。

  2. 正确嵌套:正常状况下必定要将class嵌套闭合,不然就至关于添加到全局,若是有重复命名的class就会受影响。

  3. 拒绝copy:若是想复用已有的样式,直接在原有class上用“,”语法分割,就能应用,不要再copy一份样式,会让两份样式都被应用,就要考虑样式覆盖的问题,很不友好。

  4. 滥用class:没有必要加的class不要加,每一个class的添加都应该有明确理由。滥用class的话可能会致使样式覆盖,不应应用这个样式的地方用了某个样式。

  5. 慎用 !important,会强行覆盖全部同属性样式,一旦使用后会让代码难以维护,开发过程当中绝对不要依赖该方法。以下总结了一些使用 !important的经验:

    • 必定要优化考虑使用样式规则的优先级来解决问题而不是 !important
    • 只有在须要覆盖全站或外部 css(例如引用的 ExtJs 或者 YUI )的特定页面中使用 !important
    • 解决紧急线上问题可使用,但以后也要尽快用可维护的方式将代码替换回来
    • 永远不要在全站范围的 css 上使用 !important
    • 永远不要在你的插件中使用 !important

说得容易,作起来难

破窗效应

此理论认为环境中的不良现象若是被听任存在,会诱令人们仿效,甚至变本加厉。一幢有少量破窗的建筑为例,若是那些窗不被修理好,可能将会有破坏者破坏更多的窗户。最终他们甚至会闯入建筑内,若是发现无人居住,也许就在那里定居或者纵火。一面墙,若是出现一些涂鸦没有被清洗掉,很快的,墙上就布满了乱七八糟、不堪入目的东西;一条人行道有些许纸屑,不久后就会有更多垃圾,最终人们会视若理所固然地将垃圾顺手丢弃在地上。这个现象,就是犯罪心理学中的破窗效应,在编程领域一样存在。

要作到:只要是通过你手的代码,都会比以前好一点。

最后弱弱地打个广告,个人博客,欢迎各路大神指教

参考文章:

相关文章
相关标签/搜索