资讯专栏INFORMATION COLUMN

[探索]在开发中尽量提高代码的复用性

wind3110991 / 1911人阅读

摘要:提高代码的复用性,应该是不同场景,不同解决方案的。不建议强制提高代码复用性,如果提高代码复用性会大大的降低代码的可读性,维护性,可能会得不偿失。

ctrl+c 和 ctrl+v 给我们带来了很多的便利,但是也使我们变得懒惰,不愿思考。
1.前言

相信很多人和我一样,在开发项目的时候,因为项目赶,或者一时没想到等原因。频繁使用 ctrl+c 和 ctrl+v ,导致代码很多都是重复的。这几天,也看了自己以前写的代码,简单的探索了一下,挑选几个实例,分享下如何在特定场景下,保证代码质量前提下,提高代码复用性。

提高代码的复用性,应该是不同场景,不同解决方案的。同时也要保证代码质量。不建议强制提高代码复用性,如果提高代码复用性会大大的降低代码的可读性,维护性,可能会得不偿失。
2.HTML+CSS

在做项目的时候,相信页面上总会有很多相似的按钮,比如下图,项目上几个相似的按钮。面对这样的需求,之前是直接写三个按钮



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;
}

页面调用



页面上可能还有很多按钮类似的,但是不同颜色的(如下图),也可以灵活处理,这样还可以自由组合。这个也是社区上很多 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



对于这些按钮,不建议设置 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.小结

假期看代码,提高代码复用性的总结,差不多就是这些了,当然还有一些实例,但是在之前已经写过了,和该文章提及的实例也是大同小异,就不再重复提及。提高代码的复用性是一个很大的话题,如果大家有什么好的建议,实例,欢迎分享。

-------------------------华丽的分割线--------------------

想了解更多,和我交流,内推职位,请添加我微信。或者关注我的微信公众号:守候书阁

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/98160.html

相关文章

  • [探索]开发尽量提高代码复用

    摘要:提高代码的复用性,应该是不同场景,不同解决方案的。不建议强制提高代码复用性,如果提高代码复用性会大大的降低代码的可读性,维护性,可能会得不偿失。 ctrl+c 和 ctrl+v 给我们带来了很多的便利,但是也使我们变得懒惰,不愿思考。 1.前言 相信很多人和我一样,在开发项目的时候,因为项目赶,或者一时没想到等原因。频繁使用 ctrl+c 和 ctrl+v ,导致代码很多都是重复的。这...

    Cheriselalala 评论0 收藏0
  • [探索]开发尽量提高代码复用

    摘要:提高代码的复用性,应该是不同场景,不同解决方案的。不建议强制提高代码复用性,如果提高代码复用性会大大的降低代码的可读性,维护性,可能会得不偿失。 ctrl+c 和 ctrl+v 给我们带来了很多的便利,但是也使我们变得懒惰,不愿思考。 1.前言 相信很多人和我一样,在开发项目的时候,因为项目赶,或者一时没想到等原因。频繁使用 ctrl+c 和 ctrl+v ,导致代码很多都是重复的。这...

    Keagan 评论0 收藏0
  • 设计模式 ----- 开篇

    摘要:与类型库相比,设计模式是一个更为普遍的概念。是在年,由建筑设计大师亚力山大建筑的永恒之道描述模式是一条由三部分组成的通过规则它表示了一个特定环境一类问题和一个解决方案之间的关系。设计模式是在这方面开始探索的一块里程碑。 设计模式并非类库 为了方便地编写java程序,我们会使用类库,但设计模式不是类库。 与类型库相比,设计模式是一个更为普遍的概念。类库是由程序组合...

    timger 评论0 收藏0
  • [探索] 怎样让 JS - API 具有更好实用

    摘要:下面就通过一个简单的例子,怎么让更加的实用,更好的复用。代码的实用性,只能尽量,尽量再尽量。关于实用性,命名和扩展性也很重要。而且,这样没复用性。关于这篇文章,也是我目前尝试的一种方式,如果大家有更好的一个实现方式,欢迎在评论区留言。 程序员的精神,不应不止于实现,更要注重优化。不应止于表面,更要研究内部机制,方能青出于蓝而胜于蓝。 1.前言 在上家公司开发后台管理系统的时候,频繁要处理各...

    lolomaco 评论0 收藏0
  • [探索]怎样让 JS - API 具有更好实用

    摘要:下面就通过一个简单的例子,怎么让更加的实用,更好的复用。代码的实用性,只能尽量,尽量再尽量。关于实用性,命名和扩展性也很重要。而且,这样没复用性。关于这篇文章,也是我目前尝试的一种方式,如果大家有更好的一个实现方式,欢迎在评论区留言。 程序员的精神,不应不止于实现,更要注重优化。不应止于表面,更要研究内部机制,方能青出于蓝而胜于蓝。 1.前言 在上家公司开发后台管理系统的时候,频繁要处...

    LMou 评论0 收藏0

发表评论

0条评论

wind3110991

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<