关于跨行业线智慧项目代码复用性的研究

    黄振帆 邱阳 陈伟 李帆 廖维鹏

    

    

    

    摘要:随着当今互联网产业的高速发展,智慧园区,智慧交通,智慧物流各类概念层出不穷,所应用的领域各不相同,其中却不乏包含一些通用的功能模块,若是能将这些通用的模块封装出来,成为一个基础的平台,则开发人员只需将精力投入在专业性定制化的功能模块上,能极大地提高整体的工作效率。该文将从已有项目的平台作为切入点,分析管廊、有轨电车、智慧交通的特点,并针对跨行业线智慧项目代码复用性进行研究。

    关键词:代码复用性;跨行业线;智慧项目;web;Java

    中图分类号:TP311? ? ? 文献标识码:A

    文章编号:1009-3044(2021)17-0062-04

    开放科学(资源服务)标识码(OSID):

    1 代码复用性

    1.1 为什么要提出代码复用性

    市政业务种类繁多,不同的行业所涉及的内容也不尽相同,若是每一个行业都开发一套属于自己的管理平台,固然是好事,但这其中所需投入的人员、时间、精力也是成倍地增加。若是有办法让一套代码应用于多个行业线,将繁杂的业务融合到一起,让开发人员能集中精力放在新功能的迭代上,而不是编写大量冗余的代码,从而提升工作效率,由此代码的复用性由此应运而生。

    1.2 什么是多行业线

    本文要讨论的多行业线是指多个不同或近似行业,在拥有相同基础功能的情况下,又针对各自行业线衍生出贴合自己专业性功能的平台。

    目前已有的行业线项目有:管廊,有轨电车,智慧交通。

    通用的模块包括事件管理,问题管理,发布管理,变更管理,知识库管理,设备管理,组织管理,用户管理,角色管理,菜单管理,设备类型,菜单管理,字典管理,操作日志。

    管廊行业线特有功能:管廊管理,舱室管理,分区管理,系统总览,综合监控,BIM仿真等。

    有轨电车行业线特有功能:站点管理,检测内容管理,打卡任务管理等。

    指挥交通行业线特有功能:站点管理,内容管理,车辆管理,异常情况,路线管理等。

    由上述描述我们可以发现,不同的行业线,不仅有许多业务功能相同的模块,有些行业线特有的功能也有着一定的相似程度,比如管廊行业线中的管廊,舱室,分区,与有轨电车行业线中的站点,都是对地点进行描述的字段,区别点仅在于管廊的地点分为三级,有轨电车仅一级,那么,我们是不是可以根据这一特点,合理的设计数据库,利用技术手段,将其也做到通用化,使一套代码可以适应多个行业线呢?答案是可以的。

    2 代码复用的优点

    代码复用的初衷是为了提高效率,节约重复开发的时间。

    当我们着手一个新的项目时,会发现有许多功能与我们之前的项目时相同的,若是能复用以前设计中的一些模块,一方面是可以减少一些重复性的设计工作;另外一方面,以前的模块多是经过验证了的,出问题的概率也比较小。

    当然并不是所有模块都可以不需改动很完美地复用到新项目中,不同行业的项目都会有些许改动,那么如何将这些模块融入新项目,接下来我们就会进行跨行业代码复用性的研究。

    3 代码复用的实现

    3.1 三大特性

    说到代码的复用性,首先要了解Java的三大特性:封装、继承和多态。

    封装使得每个独立的功能模块化了。我们要学会养成“分类”的习惯。这样的代码不仅结构清晰而且容易被复用。就算是熟练的程序员,也不能保证自己写过的代码没有错误,代码已经封装就可以保证它在功能范围内的正确性。这样,我们就不用浪费时间去检查和编写那些已有的功能了。良好的封装能够大大地降低模块间的耦合度。

    继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。

    多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作。多态性是对象多种表现形式的体现。

    这三个特性是实现代码复用的基础,即我们应该把更多的心思放在新的功能上,而不是反复地写一些陈旧的代码。

    3.2 常量

    在程序运行过程中,其值始终不变的量称之为常量。常量又分为整型常量,浮点型常量,小数型常量,字符型常量,布尔型常量,字符串常量。这里我们着重介绍一下字符串常量。

    字符串常量表示若干个Unicode字符组成的字符序列,使用两个英文双引号来标记,如“ABC” “管廊”都是字符串常量。要注意字符串常量与字符型常量的区別。

    3.3 枚举

    Java 枚举是一个特殊的类,一般表示一组常量。Java 枚举类使用 enum 关键字来定义,各个常量使用逗号 , 来分割。

    使用实例:

    enum Color

    {

    RED, GREEN, BLUE;

    }

    public class Test

    {

    // 执行输出结果

    public static void main(String[] args)

    {

    Color c1 = Color.RED;

    System.out.println(c1);

    }

    }

    相信有很多朋友想问为什么讲解复用性要单独介绍常量和枚举,这又与多行业线有什么联系。接下来我就为各位娓娓道来,在智慧运维管理平台中,是如何实现跨行业线的代码复用的。

    4 智慧项目的代码复用性研究

    前面我们介绍了智慧运维管理平台的三条行业线,管廊行业,有轨电车行业,智慧交通行业,如果我们不考虑代码的复用性,那么开发人员将会面临开发三套系统的窘迫,若是开发三套系统其中通用功能需要修改,每一处改动都要改三个地方,又是增加了代码出现错误的风险性,所以将系统合理的设计,成为一套灵活可配置的系统,节约开发人员的开发成本,提高开发效率,就显得尤为重要。

    首先我们来分析三条行业线共用一套代码可能会出现的问题。

    举例一:

    管廊的设备所属位置是在分区中,而管廊位置定义有三级ID,管廊,舱室,分区,也就是说如果要定位一个设备,需要有三级树形结构去定义它的位置。

    有轨电车和智慧交通都是以站点定义位置,没有管廊的三级结构。

    那么问题就出现了,在管廊项目中维护设备信息时,我们关联了管廊表、舱室表、分区表,由于篇幅关系,我们只截取片段:

    Map galleryDataMap = Maps.newHashMap();

    Map cabinDataMap = Maps.newHashMap();

    Map cabinPartitionDataMap = Maps.newHashMap();

    ……

    List returnStringList = Lists.newArrayListWithCapacity(firstIdSize);

    IntStream.range(0, firstIdSize).forEach(index -> {

    if (StringUtils.isNotBlank(thirdIds.get(index)) && cabinPartitionDataMap.containsKey(thirdIds.get(index))) {

    Map dataMap = cabinPartitionDataMap.get(thirdIds.get(index));

    returnStringList.add(new String[]{Objects.isNull(dataMap.get("gallery_name")) ? "" : dataMap.get("gallery_name").toString(),

    Objects.isNull(dataMap.get("cabin_name")) ? "" : dataMap.get("cabin_name").toString(),

    Objects.isNull(dataMap.get("cabin_part_name")) ? "" : dataMap.get("cabin_part_name").toString()});

    } else if (StringUtils.isNotBlank(secondIds.get(index)) && cabinDataMap.containsKey(secondIds.get(index))) {

    Map dataMap = cabinDataMap.get(secondIds.get(index));

    returnStringList.add(new String[]{Objects.isNull(dataMap.get("gallery_name")) ? "" : dataMap.get("gallery_name").toString(),

    Objects.isNull(dataMap.get("cabin_name")) ? "" : dataMap.get("cabin_name").toString(), ""});

    } else if (StringUtils.isNotBlank(firstIds.get(index)) && galleryDataMap.containsKey(firstIds.get(index))) {

    Map dataMap = galleryDataMap.get(firstIds.get(index));

    returnStringList.add(new String[]{Objects.isNull(dataMap.get("gallery_name")) ? "" : dataMap.get("gallery_name").toString(), "", ""});

    } else {

    returnStringList.add(new String[]{"", "", ""});

    }

    });

    return returnStringList;

    接口返回三级ID和名称,如下:

    "firstId": "e6e4c2374dd74de89c1fd8d33b609830",

    "firstName": "三亚管廊",

    "secondId": "b8050b4ca6964438b078c7b8dc3e0a49",

    "secondName": "综合舱",

    "thirdId": "3bdc3a940bf545db97961df79afcf689",

    "thirdName": "01"

    应用到項目中的效果

    而有轨电车项目和指挥交通项目中,我们只有一个站点表,不论将其主键放在firstId,secondId,thirdId中的哪一个,都无法用管廊获取名称和ID的代码去获取其相应的名称,如果用到项目中会无法正确显示。

    但是firstId,secondId,thirdId都是可以在不同项目中利用起来的字段,我们应该如何将其与有轨电车项目和指挥交通项目联系起来呢?这时候之前介绍的枚举和常量就要发挥作用了。

    首先我们选择一个字段将有轨电车项目和智慧交通项目的站点ID存入,这里我们选择secondId。

    这样我们在处理有轨电车项目和智慧交通项目时,获取站点名称就可以用secondId关联站点表。

    接下来我们建立一个包含三个行业线的枚举,方便日后维护和切换行业线。

    public enum SystemProjectEnum implements BaseStringEnum {

    UTILITYTUNNEL("utilityTunnel", "管廊"),TRAFFIC("traffic", "交通"),TRAMCAR("tramcar", "有轨电车");

    ……

    }

    建立枚举后,我们需要设置一个全局的常量类。

    /**

    * 当前项目行业线分支

    */

    public final static SystemProjectEnum CURRENT_PROJECT_ENUM = SystemProjectEnum.UTILITYTUNNEL;

    设置好常量类后,我们就可以在需要不同行业线展示不同数据的代码部分,通过判断当前系统所处行业线,进行不同的处理。

    if (EnumsUtils.isRightProject(SystemProjectEnum.UTILITYTUNNEL)) {

    ……

    } else {

    ……

    List returnStringList = Lists.newArrayListWithCapacity(secondIds.size());

    secondIds.stream().forEach(secondId -> {

    if (StringUtils.isBlank(secondId) || !dataListMap.containsKey(secondId)) {

    returnStringList.add(new String[]{null, "", null});

    return;

    }

    Map dataMap = dataListMap.get(secondId);

    returnStringList.add(new String[]{null, Objects.isNull(dataMap.get("name")) ? "" : dataMap.get("name").toString(), null});

    });

    return returnStringList;

    }

    }

    通过上面的代码我们可以看到,if (EnumsUtils.isRightProject(SystemProjectEnum.UTILITYTUNNEL))判断当前行业线,SystemProjectEnum.UTILITYTUNNEL? ? ?为管廊行业线,若当前行业线不时管廊则进入else判断内(因为有轨电车和智慧交通设计一致),最后将secondId对应的站点名称放入集合中,接口返回结果如下:

    secondId: "1"

    secondName: "杭州東站"

    应用到项目中的效果:

    由图4我们可以发现,利用设置全局常量,很好地解决了管廊设备位置和其他行业线设备位置冲突的问题。

    举例二:

    上面我们阐述了面对不同行业线,相同功能关联数据库表不同导致返回数据错误,利用全局常量来区分当前打包项目的行业线,从而返回各自行业线需要的数据,接下来我们再通过一个案例来分析。

    public void cabinPartAlarmInfoPush() {

    if (EnumsUtils.isRightProject(SystemProjectEnum.UTILITYTUNNEL)) {

    if (webSocketMap.isEmpty()) {

    return;

    }

    WebSocketData webSocketData = new WebSocketData();

    webSocketData.setType(WebSocketMessageTypeEnum.BB_MAP_GALLERY.getCode());

    Response response = Response.okAndData(WebSocketMessageTypeEnum.REQUEST.getCode(), webSocketData);

    String message = JSON.toJSONString(response, SerializerFeature.WriteMapNullValue);

    sendMessage(message);

    }

    }

    上述代码是截取了一个定时任务的一部分,利用webSocket向前端发送数据的功能,该功能只在管廊行业线启用,同举例一,还是通过判断当前系统的行业线是否为管廊,如果是,则进入if判断,发送消息给前端,否则不发送消息。若是没有个判断当前行业线的条件在,开发人员就会面临一个问题,需要开发多套系统代码,若是使用一套代码则开发管廊项目时,需要放开这个功能,开发有轨电车项目和智慧交通项目时,又要把这个功能注释掉,当项目中有许多这样的功能时,大量的注释放开操作,无形之中降低了项目的容错率,增加了时间成本,影响开发效率。而当我们使用了这个全局常量判断了当前行业线后,在管廊以外行业线在开发又或是打包时就不需要将管廊的部分注释,而当以后出现新的行业线或者老的行业线也需要发送,只需要往下添加if else即可,方便管理和维护代码,增强了代码的可扩展性,同时也让多行业线可以并存,实现的代码的复用性。

    5 结束语

    随着市政业务的不断增加,管廊,智慧交通,智慧园区,各类项目层出不穷,如果还是按以往的开发模式,按项目为单位开发,势必会造成资源无法有效利用的情况,而且多套代码对日后的维护,修改都会产生巨大的隐患,因此,开发一套成熟可复用的代码就显得尤为重要,针对具体行业线实现定制化的服务,同时又不影响其他行业线功能的实现。本文就跨行业线情况下的智慧运维平台代码复用性提出了一个解决方案,就是利用设置全局常量,指定当前开发项目的行业线,在接口中对不同行業线进行不同的处理,并且设置一个配置行业线的枚举,方便维护新旧行业线,由此一套代码可复用与各个行业线,各个业务场景,实现了代码的复用性,减少了冗余代码的出现,让开发人员更加专注地投入新功能的开发上,而不是着力于老代码的兼容,极大地提升了开发效率,节省了多行业项目的开发时间,同时具有极大的兼容性。

    参考文献:

    [1] 菜鸟教程.Java继承[EB/OL].[2020-08-24]. https://baike.baidu.com/reference/3532678/6647iBrb-WmCOqi8QzCNlFLkc-lFgPHNRAHS3J92hj2moOdnaWrz-AW3RJ3C5BBXIhSB82P6zdx7RXF16RCoAhyjjEQNkg-K9jrJ66Iv.

    [2] 一骑轻尘-宫本.Java·代码的复用之美[EB/OL].[2020-08-24].https://blog.csdn.net/qq_31428627/article/details/79125896.

    [3] PHP中文网.java 多态是什么-Java入门[EB/OL].[2020-08-24]. https://www.php.cn/java/guide/435428.html.

    [4] 于清宗."常量"与"变量"[EB/OL].[2020-08-24].https://xueshu.baidu.com/usercenter/paper/show?paperid=2395d99dd63a5a0b d27efd89037674f5&tn=SE_baiduxueshu_c1gjeupa&ie=utf-8&site=baike.

    [5] 菜鸟教程.Java枚举[EB/OL].[2020-08-24].https://baike.baidu.com/reference/3532678/6647iBrb-WmCOqi8QzCNlFLkc-lFgPHNRAHS3J92hj2moOdnaWrz-AW3RJ3C5BBXIhSB82P6zdx 7RXF16RCoAhyjjEQNkg-K9jrJ66I v.

    【通联编辑:谢媛媛】