内容简介:ctrl+c 和 ctrl+v 给我们带来了很多的便利,但是也使我们变得懒惰,不愿思考。相信很多人和我一样,在开发项目的时候,因为项目赶,或者一时没想到等原因。频繁使用 ctrl+c 和 ctrl+v ,导致代码很多都是重复的。这几天,也看了自己以前写的代码,简单的探索了一下,挑选几个实例,分享下如何在特定场景下,保证代码质量前提下,提高代码复用性。提高代码的复用性,应该是不同场景,不同解决方案的。同时也要保证代码质量。不建议强制提高代码复用性,如果提高代码复用性会大大的降低代码的可读性,维护性,可能会得
ctrl+c 和 ctrl+v 给我们带来了很多的便利,但是也使我们变得懒惰,不愿思考。
1.前言
相信很多人和我一样,在开发项目的时候,因为项目赶,或者一时没想到等原因。频繁使用 ctrl+c 和 ctrl+v ,导致代码很多都是重复的。这几天,也看了自己以前写的代码,简单的探索了一下,挑选几个实例,分享下如何在特定场景下,保证代码质量前提下,提高代码复用性。
提高代码的复用性,应该是不同场景,不同解决方案的。同时也要保证代码质量。不建议强制提高代码复用性,如果提高代码复用性会大大的降低代码的可读性,维护性,可能会得不偿失。
2.HTML+CSS
在做项目的时候,相信页面上总会有很多相似的按钮,比如下图,项目上几个相似的按钮。面对这样的需求,之前是直接写三个按钮
<button type="button" class='u-btn-yes-samll'>确定</button> <button type="button" class='u-btn-yes'>确定</button> <button type="button" class='u-btn-yes-big'>确定</button> 复制代码
css
button{ border:none; font-family:'微软雅黑'; } .u-btn-yes{ width:80px; height:36px; font-size:14px; color:#fff; border-radius:10px; background:#09f; } .u-btn-yes-samll{ width:60px; height:30px; font-size:12px; color:#fff; border-radius:10px; background:#09f; } .u-btn-yes-big{ width:100px; height:40px; font-size:16px; color:#fff; border-radius:10px; background:#09f; } 复制代码
这样相当于每增加一种按钮,就增加了几行的 css 代码,实际上改变的只有 width ,height ,font-size 这三个属性。
实际上可以根据大小进行代码复用。
.u-btn-yes{ width:80px; height:36px; font-size:14px; color:#fff; border-radius:10px; background:#09f; } .u-btn-yes.u-btn-samll{ width:60px; height:30px; font-size:12px; } .u-btn-yes.u-btn-big{ width:100px; height:40px; font-size:16px; } 复制代码
页面调用
<button type="button" class='u-btn-yes u-btn-samll'>确定</button> <button type="button" class='u-btn-yes'>确定</button> <button type="button" class='u-btn-yes u-btn-big'>确定</button> 复制代码
页面上可能还有很多按钮类似的,但是不同颜色的(如下图),也可以灵活处理,这样还可以自由组合。这个也是社区上很多 UI 库所使用的方式。
.u-btn{ width:80px; height:36px; font-size:14px; color:#fff; border-radius:10px; background:#09f; } .u-btn-samll{ width:60px; height:30px; font-size:12px; } .u-btn-big{ width:100px; height:40px; font-size:16px; } .u-btn-red{ background:#f33; } .u-btn-yellow{ background:#f90; } 复制代码
html
<button type="button" class='u-btn u-btn-samll'>确定</button> <button type="button" class='u-btn u-btn-red'>确定</button> <button type="button" class='u-btn u-btn-big u-btn-yellow'>确定</button> 复制代码
对于这些按钮,不建议设置 margin ,positon 等样式,因为不同的按钮在不同的地方,上面这几个属性基本不会一样。
3.JavaScript
关于提高代码复用性的好处,在上面 HTML+CSS的实例里面并没有很明显的优势,但在 JS 里面提高代码的复用性优势就比较明显了,下面简单列举几个例子。
3-1.使用装饰者模式进行封装
在上家公司的项目里面有这样代码,目的也很明显,就是用户填写表单的时候,还没有填写完整就提交,前端这里就需要给一个简单的提示。
//当信息没填写完整的时候弹出提示 layer.alert('请检查信息是否填写完整',{ title:'提示', icon:2 }) 复制代码
基于 layer 这个开源库,代码看得特别的简单。但是随着项目的开发,用户填写表单的地方有多个,那么上面的代码就会被复制到多个地方,这样难免会有有点多余。
另外,这样做最大的一个问题就是:如果上面的代码在项目上有20个地方在用,有一天需求变了,title 这个属性值要从‘提示’变成‘警告’。那就麻烦了,要找20个地方,即使编辑器有全局替换的功能,这样的改动出问题的概率也比较大。面对这样的情况。之前处理的方法是对这个弹窗进行简单粗暴的封装,方便复用。
function openTips(){ //当信息没填写完整的时候弹出提示 layer.alert('请检查信息是否填写完整',{ title:'提示', icon:2 }); } 复制代码
在需要的地方,需要的时候进行调用就好,这样可以写少很多代码!修改起来,也只需要修改 openTips 这一个地方就完事了。
openTips(); 复制代码
3-2.使用策略模式代替 switch
下面再看一个实例。借用下之前群友的发的代码。
模拟数据
let listWarnConf = [ { warnType: 1, warnCondition: 220, }, { warnType: 2, warnCondition: 36, }, { warnType: 3, warnCondition: 45, }, { warnType: 4, warnCondition: 110, }, { warnType: 5, warnCondition: 380, } ] 复制代码
业务逻辑代码
listWarnConf.forEach(item => { switch(item.warnType) { case 1: item.warnTypeText = '超压'; item.warnConditionText = `电压高于${item.warnCondition}V` break; case 2: item.warnTypeText = '欠压'; item.warnConditionText = `电压低于${item.warnCondition}V` break case 3: item.warnTypeText = '超载'; item.warnConditionText = `电流高于${item.warnCondition}A` break case 4: item.warnTypeText = '电压不平衡'; item.warnConditionText = `电压不平衡高于${item.warnCondition}%` break case 5: item.warnTypeText = '电流不平衡'; item.warnConditionText = `电流不平衡${item.warnCondition}%` break } }) 复制代码
这样看着结果是没问题的,但是看着那么多 case 执行的都是赋值操作。而且最大的问题和上面一样,如果多个地方使用,需求变了,那么还是要修改这么多的地方,下面优化下,让代码的复用性提高下。
//设置配置数据 let warnConfig={ 1:{ warnTypeText:'超压', warnConditionText:'电压高于replaceTextV' }, 2:{ warnTypeText:'欠压', warnConditionText:'电压低于replaceTextV' }, 3:{ warnTypeText:'超载', warnConditionText:'电流高于replaceTextV' }, 4:{ warnTypeText:'电压不平衡', warnConditionText:'电压不平衡高于replaceText%' }, 5:{ warnTypeText:'电流不平衡', warnConditionText:'电流不平衡高于replaceText%' } } //业务逻辑--根据配置数据设置warnTypeText和warnConditionText listWarnConf.forEach(item => { item.warnTypeText=warnConfig[item.warnType].warnTypeText; item.warnConditionText=warnConfig[item.warnType].warnConditionText.replace('replaceText',item.warnCondition); }) 复制代码
这样改代码量没减少,可读性比 switch 差,但能读懂。但是这样做就是重复的代码少了,配置数据和业务逻辑分离了,如果以后要修改配置数据或者业务逻辑,就修改其中一项即可,互相不影响。把配置数据抽出来公用,那么在需要修改的时候,直接修改就好。
关于提高代码的复用性,或者说减少重复的代码,个人觉可以往以下目标努力--当需求发生改变,需要修改代码的时候,同样的代码不要修改两次。
3-3.保持函数单一职责,灵活组合
保持函数的单一职责,保证一个函数只执行一个动作,每个动作互不影响,可以自由组合,就可以提高代码的复用性。
比如下面的代码,从服务端请求回来的订单数据如下,需要进行以下处理 1.根据 status 进行对应值得显示(0-进行中,1-已完成,2-订单异常) 2.把 startTime 由时间戳显示成 yyyy-mm-dd 3.如果字段值为空字符串 ,设置字段值为 ‘--’
let orderList=[ { id:1, status:0, startTime:1538323200000, }, { id:2, status:2, startTime:1538523200000, }, { id:3, status:1, startTime:1538723200000, }, { id:4, status:'', startTime:'', }, ]; 复制代码
需求似乎很简单,代码也少
let _status={ 0:'进行中', 1:'已完成', 2:'订单异常' } orderList.forEach(item=>{ //设置状态 item.status=item.status.toString()?_status[item.status]:''; //设置时间 item.startTime=item.startTime.toString()?new Date(item.startTime).toLocaleDateString().replace(/\//g,'-'):''; //设置-- for(let key in item){ if(item[key]===''){ item[key]='--'; } } }) 复制代码
运行结果也正常,但是这样写代码重复性会很多,比如下面,另一组重服务端请求回来的用户数据,用户数据没有 status,startTime,两个字段,而且需要根据 type 对应显示用户的身份(0-普通用户,1-vip,2-超级vip)。
let userList=[ { id:1, name:'守候', type:0 }, { id:2, name:'浪迹天涯', type:1 }, { id:3, name:'曾经', type:2 } ] 复制代码
出现这样的需求,之前写的代码无法重用,只能复制过来,再修改下。
let _type={ 0:'普通用户', 1:'vip', 2:'超级vip' } userList.forEach(item=>{ //设置type item.type=item.type.toString()?_type[item.type]:''; //设置-- for(let key in item){ if(item[key]===''){ item[key]='--'; } } }) 复制代码
结果正常,想必大家已经发现问题了,代码有点多余。下面就使用单一职责的原则改造下操作函数,设置 status,startTime,type,-- 。这里拆分成四个函数。
let handleFn={ setStatus(list){ let _status={ 0:'进行中', 1:'已完成', 2:'订单异常' } list.forEach(item=>{ item.status=item.status.toString()?_status[item.status]:''; }) return list }, setStartTime(list){ list.forEach(item=>{ item.startTime=item.startTime.toString()?new Date(item.startTime).toLocaleDateString().replace(/\//g,'-'):''; }) return list; }, setInfo(list){ list.forEach(item=>{ for(let key in item){ if(item[key]===''){ item[key]='--'; } } }) return list; }, setType(list){ let _type={ 0:'普通用户', 1:'vip', 2:'超级vip' } list.forEach(item=>{ item.type=item.type.toString()?_type[item.type]:''; }) return list; } } 复制代码
下面直接调用函数就好
//处理订单数据 orderList=handleFn.setStatus(orderList); orderList=handleFn.setStartTime(orderList); orderList=handleFn.setInfo(orderList); console.log(orderList); //处理用户数据 userList=handleFn.setType(userList); userList=handleFn.setInfo(userList); console.log(userList); 复制代码
运行结果也正常
如果嫌弃连续赋值麻烦,可以借用 jQuery 的那个思想,进行链式调用。
let ec=(function () { let handle=function (obj) { //深拷贝对象 this.obj=JSON.parse(JSON.stringify(obj)); }; handle.prototype={ /** * @description 设置保密信息 */ setInfo(){ this.obj.map(item=>{ for(let key in item){ if(item[key]===''){ item[key]='--'; } } }); return this; }, /** * @description 设置状态 */ setStatus(){ let _status={ 0:'进行中', 1:'已完成', 2:'订单异常' } this.obj.forEach(item=>{ item.status=item.status.toString()?_status[item.status]:'' }); return this; }, /** * @description 设置时间 */ setStartTime(){ this.obj.forEach(item=>{ item.startTime=item.startTime.toString()?new Date(item.startTime).toLocaleDateString().replace(/\//g,'-'):''; }); return this; }, /** * @description 设置type */ setType(){ let _type={ 0:'普通用户', 1:'vip', 2:'超级vip' } this.obj.forEach(item=>{ item.type=item.type.toString()?_type[item.type]:''; }) return this; }, /** * @description 返回处理结果 * @return {Array|*} */ end(){ return this.obj; } } //暴露构造函数接口 return function (obj) { return new handle(obj); } })(); 复制代码
这样就可以链式调用了
//处理订单数据 orderList=ec(orderList).setStatus().setStartTime().setInfo().end(); console.log(orderList); //处理用户数据 userList=ec(userList).setType().end(); console.log(userList); 复制代码
事情到这里了,相信大家发现一个很严重的问题就是循环的次数增加了。没优化之前,只需要循环一次,就可以把设置状态,设置时间,设置--这些步骤都完成,但是现在 setStatus().setStartTime().setInfo()
这里的代码,每执行一个函数,都遍历了一次数组,这个就得优化下。处理的方式就是在每一个函数里面,只记录要处理什么,但是不进行处理,等到执行到 end 的时候再统一处理,以及返回。
let ec=(function () { let handle=function (obj) { //深拷贝对象 this.obj=JSON.parse(JSON.stringify(obj)); //记录要处理的步骤 this.handleFnList=[]; }; handle.prototype={ /** * @description 设置保密信息 */ handleSetInfo(item){ for(let key in item){ if(item[key]===''){ item[key]='--'; } } return this; }, setInfo(){ this.handleFnList.push('handleSetInfo'); return this; }, /** * @description 设置状态 */ handleSetStatus(item){ let _status={ 0:'进行中', 1:'已完成', 2:'订单异常' } item.status=item.status.toString()?_status[item.status]:'' return item; }, setStatus(){ this.handleFnList.push('handleSetStatus'); return this; }, /** * @description 设置时间 */ handleSetStartTime(item){ item.startTime=item.startTime.toString()?new Date(item.startTime).toLocaleDateString().replace(/\//g,'-'):''; return item; }, setStartTime(){ this.handleFnList.push('handleSetStartTime'); return this; }, /** * @description 设置type */ handleSetType(item){ let _type={ 0:'普通用户', 1:'vip', 2:'超级vip' } item.type=item.type.toString()?_type[item.type]:''; return item; }, setType(){ this.handleFnList.push('handleSetType'); return this; }, /** * @description 返回处理结果 * @return {Array|*} */ end(){ //统一处理操作 this.obj.forEach(item=>{ this.handleFnList.forEach(fn=>{ item=this[fn](item); }) }) return this.obj; } } //暴露构造函数接口 return function (obj) { return new handle(obj); } })(); 复制代码
这样改,之前的调用方式不需要改变,然后结果也是正确的
可能大家会觉得很简单一个需求,却搞得这么复杂。如果这样想是正确的,因为这个的确搞复杂了,可读性也差了,但想到项目遇到的处理数据不止这一些,还有比如金额的格式显示,其它数据的各种状态码解析显示,银行卡号每隔4位分割,电话号码的显示等等。所以就先封装一下,以后用的时候,直接使用。不知道算不算是先苦后甜?如果需求比较简单,可能真的没必要这么样封装。
4.小结
假期看代码,提高代码复用性的总结,差不多就是这些了,当然还有一些实例,但是在之前已经写过了,和该文章提及的实例也是大同小异,就不再重复提及。提高代码的复用性是一个很大的话题,如果大家有什么好的建议,实例,欢迎分享。
-------------------------华丽的分割线--------------------
想了解更多,和我交流,内推职位,请添加我微信。或者关注我的微信公众号:守候书阁
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Rails 5敏捷开发
[美] Sam Ruby、[美] Dave Thomas、[美] David Heinemeier Hansson / 安道、叶炜、大疆Ruby技术团队 / 华中科技大学出版社 / 2017-12-30 / 115.00
本书以讲解“购书网站”案例为主线,逐步介绍Rails的内置功能。全书分为3部分,第一部分介绍Rails的安装、应用程序验证、Rails框架的体系结构,以及Ruby语言知识;第二部分用迭代方式构建应用程序,然后依据敏捷开发模式开展测试,最后用Capistrano完成部署;第三部分补充日常实用的开发知识。本书既有直观的示例,又有深入的分析,同时涵盖了Web开发各方面的知识,堪称一部内容全面而又深入浅出......一起来看看 《Rails 5敏捷开发》 这本书的介绍吧!