提高代码质量的目的
程序猿的本职工作就是写代码,写出高质量的代码应该是我们的追求和对自己的要求,因为:
高质量的代码往往意味着更少的BUG,更好的模块化,是我们扩展性,复用性的基础
高质量的代码也意味着更好的书写,更好的命名,有利于我们的维护
什么代码算好的质量怎样来定义代码质量的"好",业界有很多标准,本文认为好的代码应该有以下特点:
代码整洁,比如缩进之类的,现在有很多工具可以自动解决这个问题,比如eslint。
结构规整,没有漫长的结构,函数拆分合理,不会来一个几千行的函数,也不会有几十个if...else。这要求写代码的人有一些优化的经验,本文会介绍几种模式来优化这些情况。
阅读起来好理解,不会出现一堆a,b,c这种命名,而是应该尽量语义化,变量名和函数名都尽量有意义,最好是代码即注释,让别人看你的代码就知道你在干嘛。
本文介绍的设计模式主要有策略/状态模式,外观模式,迭代器模式,备忘录模式。
策略/状态模式 策略模式基本结构假如我们需要做一个计算器,需要支持加减乘除,为了判断用户具体需要进行哪个操作,我们需要4个if...else来进行判断,如果支持更多操作,那if...else会更长,不利于阅读,看着也不优雅。所以我们可以用策略模式优化如下:
function calculator(type, a, b) { const strategy = { add: function(a, b) { return a + b; }, minus: function(a, b) { return a - b; }, division: function(a, b) { return a / b; }, times: function(a, b) { return a * b; } } return strategy[type](a, b); } // 使用时 calculator('add', 1, 1);上述代码我们用一个对象取代了多个if...else,我们需要的操作都对应这个对象里面的一个属性,这个属性名字对应我们传入的type,我们直接用这个属性名字就可以获取对应的操作。
状态模式基本结构状态模式和策略模式很像,也是有一个对象存储一些策略,但是还有一个变量来存储当前的状态,我们根据当前状态来获取具体的操作:
function stateFactor(state) { const stateObj = { status: '', state: { state1: function(){}, state2: function(){}, }, run: function() { return this.state[this.status]; } } stateObj.status = state; return stateObj; } // 使用时 stateFactor('state1').run();if...else其实是根据不同的条件来改变代码的行为,而策略模式和状态模式都可以根据传入的策略或者状态的不同来改变行为,所有我们可以用这两种模式来替代if...else。
实例:访问权限这个例子的需求是我们的页面需要根据不同的角色来渲染不同的内容,如果我们用if...else写就是这样:
// 有三个模块需要显示,不同角色看到的模块应该不同 function showPart1() {} function showPart2() {} function showPart3() {} // 获取当前用户的角色,然后决定显示哪些部分 axios.get('xxx').then((role) => { if(role === 'boss'){ showPart1(); showPart2(); showPart3(); } else if(role === 'manager') { showPart1(); showPart2(); } else if(role === 'staff') { showPart3(); } });上述代码中我们通过API请求获得了当前用户的角色,然后一堆if...else去判断应该显示哪些模块,如果角色很多,这里的if...else就可能很长,我们可以尝试用状态模式优化下:
// 先把各种角色都包装到一个ShowController类里面 function ShowController() { this.role = ''; this.roleMap = { boss: function() { showPart1(); showPart2(); showPart3(); }, manager: function() { showPart1(); showPart2(); }, staff: function() { showPart3(); } } } // ShowController上添加一个实例方法show,用来根据角色展示不同的内容 ShowController.prototype.show = function() { axios.get('xxx').then((role) => { this.role = role; this.roleMap[this.role](); }); } // 使用时 new ShowController().show();上述代码我们通过一个状态模式改写了访问权限模块,去掉了if...else,而且不同角色的展示都封装到了roleMap里面,后面要增加或者减少都会方便很多。
实例:复合运动这个例子的需求是我们现在有一个小球,我们需要控制他移动,他移动的方向可以是上下左右,还可以是左上,右下之类的复合运动。如果我们也用if...else来写,这头都会写大:
// 先来四个方向的基本运动 function moveUp() {} function moveDown() {} function moveLeft() {} function moveRight() {} // 具体移动的方法,可以接收一个或两个参数,一个就是基本操作,两个参数就是左上,右下这类操作 function move(...args) { if(args.length === 1) { if(args[0] === 'up') { moveUp(); } else if(args[0] === 'down') { moveDown(); } else if(args[0] === 'left') { moveLeft(); } else if(args[0] === 'right') { moveRight(); } } else { if(args[0] === 'left' && args[1] === 'up') { moveLeft(); moveUp(); } else if(args[0] === 'right' && args[1] === 'down') { moveRight(); moveDown(); } // 后面还有很多if... } }