当前位置: 首页 > article >正文

代码重构 - 规范

事物的复杂程度在很大程度上是取决于其有序程度,减少无序能在一定程度上降低复杂度,这正是规范的价值所在。通过规范,把无序的混沌控制在一个合理的范围内,从而帮助我们减少认知成本,降低对事物认知的复杂度。

代码规范

代码格式

代码格式关系到代码的可读性,因此需要遵从一定的规范,包括缩进,水平对齐,注释格式等,关于代码格式,一个团队中最好是选定一种格式,因为一致性可以减少复杂度。

空行规范

空行是一个很小的细节,但不仅仅是一个细节问题。

老子的《道德经》中有提到 “三十辐共一毂,当其无,有车之用”,意思是指正是因为有了车轮毂和车轴之间的空白,车轮能够转起来,这正是“无“的价值。

空行在代码隔断方面作用也很大,例如spring中的BeanDefinitionVisitor,在包声明,导入声明和每个函数之间都有空行隔开,这种极其简单的规则极大地影响代码的视觉体验,每隔空白行都是一条线索,提示你下一组代码表示的是不同的概念和功能。

package org.springframework.beans.factory.config;



import java.util.LinkedHashMap;

import java.util.LinkedHashSet;

import java.util.List;

import java.util.Map;

import java.util.Set;



import org.springframework.beans.MutablePropertyValues;

import org.springframework.beans.PropertyValue;

import org.springframework.lang.Nullable;

import org.springframework.util.Assert;

import org.springframework.util.ObjectUtils;

import org.springframework.util.StringValueResolver;



public class BeanDefinitionVisitor {



       @Nullable

       private StringValueResolver valueResolver;





       public BeanDefinitionVisitor(StringValueResolver valueResolver) {

              Assert.notNull(valueResolver, "StringValueResolver must not be null");

              this.valueResolver = valueResolver;

       }



       protected BeanDefinitionVisitor() {

       }





       public void visitBeanDefinition(BeanDefinition beanDefinition) {

              visitParentName(beanDefinition);

              visitBeanClassName(beanDefinition);

              visitFactoryBeanName(beanDefinition);

              visitFactoryMethodName(beanDefinition);

              visitScope(beanDefinition);

              if (beanDefinition.hasPropertyValues()) {

                     visitPropertyValues(beanDefinition.getPropertyValues());

              }

              if (beanDefinition.hasConstructorArgumentValues()) {

                     ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();

                     visitIndexedArgumentValues(cas.getIndexedArgumentValues());

                     visitGenericArgumentValues(cas.getGenericArgumentValues());

              }

       }



       protected void visitParentName(BeanDefinition beanDefinition) {

              String parentName = beanDefinition.getParentName();

              if (parentName != null) {

                     String resolvedName = resolveStringValue(parentName);

                     if (!parentName.equals(resolvedName)) {

                            beanDefinition.setParentName(resolvedName);

                     }

              }

       }

}

如果删掉这些空白行,代码可读性就会变得很差。

package org.springframework.beans.factory.config;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringValueResolver;
public class BeanDefinitionVisitor {
       @Nullable
       private StringValueResolver valueResolver;
       public BeanDefinitionVisitor(StringValueResolver valueResolver) {
              Assert.notNull(valueResolver, "StringValueResolver must not be null");
              this.valueResolver = valueResolver;
       }
protected BeanDefinitionVisitor() {
}
public void visitBeanDefinition(BeanDefinition beanDefinition) {
              visitParentName(beanDefinition);
              visitBeanClassName(beanDefinition);
              visitFactoryBeanName(beanDefinition);
              visitFactoryMethodName(beanDefinition);
              visitScope(beanDefinition);
              if (beanDefinition.hasPropertyValues()) {
                     visitPropertyValues(beanDefinition.getPropertyValues());
              }
              if (beanDefinition.hasConstructorArgumentValues()) {
                     ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
                    visitIndexedArgumentValues(cas.getIndexedArgumentValues());
                    visitGenericArgumentValues(cas.getGenericArgumentValues());
              }
       }
       protected void visitParentName(BeanDefinition beanDefinition) {
              String parentName = beanDefinition.getParentName();
              if (parentName != null) {
                     String resolvedName = resolveStringValue(parentName);
                     if (!parentName.equals(resolvedName)) {
                            beanDefinition.setParentName(resolvedName);
                     }
              }
       }
}

在工作中可以使用idea中的代码格式化快捷键帮助我们快速格式化,但是工具毕竟是工具,有些功能逻辑还是需要开发者自己把握。一个简单的原则就是将概念相关的代码放在一起,相关性越强,彼此之间的距离就越近。

命名规范

上一章主要讲了如何命名及命名的意义,这里主要介绍一下基本的命名规范。

在Java中,有一下默认的命名约定:

类名采用大驼峰形式,取首字母大写的驼峰,例如Object,User,String等。

方法名采用小驼峰,取首字母小写的驼峰,一般为动宾结构,例如sleepThreeSeconds,

appendText。

常量名的字母全部大写,单词之间用下划线连接,例如TOTAL_COUNT,PAGE_SIZE等。

枚举类以Enum或Type结尾,枚举类成员名称全部大写,单词之间用下划线连接,例如SexEunm.MALE,SexEnum.FEMALE。

抽象类一般以Abstract开头,异常类使用Exception结尾,实现类以Impl结尾,测试类以需要测试的类名开头,Test结尾。

包名一般全部小写,不同单词之间用 . 连接。

日志规范

日志的重要性常常被人忽略,写好日志可以帮我们大大减轻后期维护的压力。

一般情况,将日志分为四个级别,由高到低:Error,Warn,Info,Debug。

Error日志表示是无法回复的错误,需要立即处理,比如说数据库连接错误,IO错误,未知的系统错误等,对于这类日志,不仅仅要打出错误堆栈,还需要打出上下文的一些参数,或者是功能模块的一些信息,便于排查。

系统中普遍都会接入监控系统,Error级别的日志一般是需要人工介入的,所以不能滥用,否则会增加系统的维护成本。

Warn日志表示一些可预知的问题,比如说参数错误,权限异常等等。在监控中一般会配置在一定的时间区间内warn日志出现了多少次的阈值告警。这样可以及时跟进问题。

Info日志通常用来记录一些系统运行中的状态。在通过日志定位问题时,优先通过info去初步定位,但是切记,不要把info日志当成debug来使用,防止记录的数据过多,让开发者找不到关键的信息。

Debug日志表示开发过程中的一些调试信息。例如某些参数需要打印下来,就可以用这类日志。通常在开发和测试环境允许debug日志的打印,但是到了线上就会关闭,防止日志输出量特别大。

public void destroy() {

        try {

            log.debug("====关闭后台任务任务线程池====");

            Threads.shutdownAndAwaitTermination(scheduledExecutorService);

        } catch (Exception e) {

            log.error(e.getMessage(), e);

        }



}
异常规范

异常规范主要包含异常处理和错误码设置两个方面。

一般建议在系统中将异常分为系统异常(SystemException)和业务异常(BizException)两种,不要在系统中胡乱地插入try-catch,会搞坏代码结构,将错误处理和正常流程混为一谈,严重影响代码的可读性。

    try{

            Response res = process(request);

        }catch (BizException e){

            log.error("BizException with error code:{},msg:{}",e.getErrorCode(),e.getErrorMsg());

        }catch (SystemException ex){

            log.error("System error {},{}",ex.getMessage(),ex);

        }catch (Exception ex){

            log.error("System error {},{}",ex.getMessage(),ex);

        }

错误码非常重要,需要统一的约定,对于错误码的管理,在后续的系统维护中会显得很轻松。

这里主要介绍两种错误码的约定方式:编号错误码和显性化错误码。

编号错误码好处是编码风格固定,给人一种正式感,缺点就是必须要配合文档才能理解错误码的含义。例如Oracle中共有两千多种错误码,从ORA-000001~ORA-02149,每一个错误码都有对应的错误解释。

ORA-00001:违反唯一约束条件

ORA-00018:超过最大会话数

ORA-00024:单一进程模式下不允许从多个线程注册

需要注意的是,一定要预留足够的码号,例如淘宝开放平台所用的3位数就显得很拘谨,其支撑的错误数最多不能超过三位数,超过后,为了向后兼容,只能通过子错误码的方式进行处理。

显性化的错误更具有灵活性,适合敏捷开发。例如,将错误码定义成3个部分:类型+场景+标识,每个部分之间使用下划线隔开,我们可以做个约定:P代表参数异常,B代表业务异常,S代表系统异常,那么错误码的设置就可以如下:

参数异常:P_xx_xx(P_Customer_NameIsNull:客户姓名不能为空)

业务异常:B_xx_xx(B_Customer_NameAlreadyExist:客户姓名已存在)

系统异常:S_xx_xx(S_Unknow_Error:未知系统错误)

如果业务应用的错误都用这种约定来描述和表达,那么只要大家遵守相同的规范,系统的可维护性和可理解性就会大大提升。

防止破窗

破窗效应是指环境中的不良现象如果被放任存在,就会诱使人们效仿,甚至变本加厉。

在软件开发过程中,破窗效应屡见不鲜,面对一个混乱的系统和一段杂乱无章的代码,后来人们往往会加入更多的垃圾代码,这也凸显了规范和架构的重要性。首先,我们要有一套规范,并且遵守,不去做那打破第一扇窗的人;其次,破窗要及时修复,不要让事情进一步恶化。整洁的代码需要每一个人的精心呵护,需要整个团队都具备一些工匠精神。


http://www.kler.cn/a/473239.html

相关文章:

  • 第四、五章补充:线代本质合集(B站:小崔说数)
  • 【网络协议】静态路由详解
  • STM32-笔记37-吸烟室管控系统项目
  • DuckDB:PRAGMA语句动态配置数据库行为
  • 单片机实现模式转换
  • PHP进阶-在Ubuntu上搭建LAMP环境教程
  • 【Dify】Dify自定义模型设置 | 对接DMXAPI使用打折 Openai GPT 或 Claude3.5系列模型方法详解
  • SAP销售订单与MRP的另一个关联点:需求类型
  • <代码随想录> 算法训练营-2025.01.04
  • 动手学深度学习11.4. 随机梯度下降-笔记练习(PyTorch)
  • JavaScript系列(14)--元编程技术
  • WebSocket 服务端开发:Node.js 实战
  • 备战春招—FPGA 2024年的面试题库
  • 网络传输层TCP协议
  • Java-编写的一个生产者-消费者模式
  • docker-compose部署下Fastapi中使用sqlalchemy和Alembic
  • CST软件如何设置分布式计算(Distributed Computing)的 TCP-IP子网
  • Redis 笔记(二)-Redis 安装及测试
  • (长期更新)《零基础入门 ArcGIS(ArcScene) 》实验七----城市三维建模与分析(超超超详细!!!)
  • 运行vue项目,显示“npm”无法识别为 cmdlet、函数、脚本文件或可操作程序的名称
  • 腾讯云AI代码助手-每日清单助手
  • Python----Python爬虫(selenium的使用,定位元素,层级定位)
  • 每日一题-两个链表的第一个公共结点
  • 阿里云人工智能平台图像视频特征提取
  • python注意事项:range遍历越索引现象、列表边遍历边修改出现的问题
  • 车载软件架构 --- 关于ARXML文件那点事