不一样的代码整洁之道

Tech 导读
本文旨在提供一种代码整洁方法的思想理论,读者可以通过本文在理论的指导下灵活运用技术类方法。
导读
自然语言(Natural language)通常是指一种自然地随文化演化的语言,自然语言是人类交流和思维的主要工具。与自然语言相对的是逻辑语言,自然语言是人脑与人脑的交际工具 ,逻辑语言是人脑与电脑的交际工具,计算机语言具备自然语言人脑与人脑的交际又具备逻辑语言人脑与电脑的交际。人脑与人脑的交际如要高效需以最简洁的表达,如:
2.1 大道至简
【Java】//key = sNo_dNo_bTypeprivate Map<String, Processor> processorMap = new HashMap<>();//糟糕实现private Processor getProcessor(DataDto dataDto) {//无必要的循环 未理解hash表原理for (Map.Entry<String, Processor> entry : processorMap.entrySet()){if (entry.getKey().contains(dataDto.getSellerNo())){String[] keyArr = entry.getKey().split("_");if (keyArr[1].contains(dataDto.getDeptNo()) && keyArr[2].equals(dataDto.getBizType())) {return entry.getValue();}}}return null;}//优秀实现private Processor getProcessor(DataDto dataDto) {String key = dataDto.getSNo() + "_" + dataDto.getDNo() + "_" + dataDto.getBType();return processor = processorMap.get(key);}
解析:
1.通过阅读两种实现代码,优秀实现更容易理解,逻辑更清晰。
2.“糟糕实现”原因在于未真正理解MashMap数据结构原理,采用了线性查找方式,将逻辑实现变得复杂。
3.代码层级嵌套,且对key的处理逻辑分散。
实践建议:对于不同数据结构实现应优先选择配套的读写实现,“糟糕实现”中hash数据结构实现却使用了线性访问方式。
2.2 站在巨人的肩膀上
【Java】//对null对象验证//普通实现public String trim(String message) {if (message != null) {return message.trim();}return message;}//优秀实现public String trim(String message) {return Optional.ofNullable(message).orElse("").trim();}//对null&“”验证//普通实现if (message != null && !"".equals(message)) {return message.trim();}//优秀实现if(StringUtils.isNotEmpty(message)){return message.trim();}//对List数据分组List<String> nameList = new ArrayList<>(Arrays.asList("A-name","B-name","C-name","A-name"));//普通实现Map<String, List<String>> map = new HashMap<>();for (String name : nameList) {String[] split = name.split("-");List<String> names = map.get(split[0]);if (null == names) {names = new ArrayList<>();}names.add(name);map.put(split[0], names);}//优秀实现Map<String, List<String>> nameMap = nameList.stream().collect(Collectors.groupingBy(o -> o.split("-")[0]));
解析:
1.通过使用高版本JDK和工具包(巨人的肩膀)使用代码逻辑更新简单、明了;
2.使用工具类实现逻辑一致,基本已具备常识储备,阅读代码时无须再理解逻辑。
实践建议:优先使用工具类提供的方法,经过多次验证的代码出现Bug的概率更低,将无必要的重复造轮子的时间专注与核心业务。
2.3 合理使用语言特性
【Java】//创建对象//普通实现public class Personnel {/*** 姓名 必填*/private String name;/*** 年龄*/private int age;/*** 性别*/private int sex;/*** 身份证号 必填*/private String idNumber;/*** 地址*/private String address;}//无法明确对象必填数据与可选数据//当对象属性较多时 容易遗漏必填数据//错误会被延迟到测试阶段Personnel personnel = new Personnel()personnel.setName("张三");personnel.setIdNumber("123123123123123123");//优秀实现public class Personnel {/*** 姓名 必填*/private String name;/*** 年龄*/private int age;/*** 性别*/private int sex;/*** 身份证号 必填*/private String idNumber;/*** 地址*/private String address;/*** @deprecated 使用带参数的构造方法*/@Deprecatedpublic Personnel() {}//利用构造函数提示必填属性public Personnel(String name, String idNumber) {this.name = name;this.idNumber = idNumber;}}//明确对象必填数据与可选数据//减少因必填数据遗漏所造成的重复测试//当属性较多时可采用build类优化构造方法参数过的问题Personnel personnel = new Personnel("张三","123123123123123123")//普通实现public class Response {protected String requestNo;protected int code;protected String message;/*** @Deprecated 使用带参数的构造方法* {@link ResponseBuild#success(String)}* {@link ResponseBuild#error(String, StatusCode)}*/public Response() {}public Response(String requestNo, int code, String message) {this.requestNo = requestNo;this.code = code;this.message = message;}}//返回成功//Response response = new Response();Response response = new Response("123213",200,"成功");if(失败){response.getCode(300);response.setMessage("失败")}else{response.getCode(200);response.setMessage("成功")}//优秀实现public final class WebResponseBuild {private WebResponseBuild() {throw new IllegalStateException("Utility class");}public static <T> WebResponse<T> success(String requestNo) {return new WebResponse<>(requestNo, DEFAULT_SUCCESS.getCode(), DEFAULT_SUCCESS.getMessage(), null);}public static <T> WebResponse<T> success(String requestNo, T data) {return new WebResponse<>(requestNo, DEFAULT_SUCCESS.getCode(), DEFAULT_SUCCESS.getMessage(), data);}public static <T> WebResponse<T> error(String requestNo, StatusCode messageCode) {return new WebResponse<>(requestNo, messageCode.getCode(), messageCode.getMessage(), null);}public static <T> WebResponse<T> error(String requestNo, int code, String message) {return new WebResponse<>(requestNo, code, message, null);}}if(失败){return WebResponseBuild.error("123213",StatusCode.PARAMETER_IS_NULL)}return WebResponseBuild.success("123213")personnel.setName("张三");
解析:
1.Java在创建对象指定必要参数是友好API的一种表现。在无独立配套说明文档的情况能明确对象创建条件,避免代码与文档脱节情况造成的使用难度增加。
2.减少相似代码( personnel.setName("张三") )所占主逻辑篇幅,使主逻辑的逻辑保持清晰。
3.封装API工具类,降低API使用成本,客户端更轻。因为没有服务提供者更清楚业务逻辑。
实践建议:提供更易使用的API,可以降低对API文档的依赖性,代码既文档。
2.4 保持代码卷面整洁
【Java】//逻辑说明:只保留 PD 属性数据//普通实现private void removeBatch(PoBatchRel batchRel) {//相似的逻辑就高度的聚合if (batchRel == null) {return;}if (CollectionUtils.isEmpty(batchRel.getBatchAttrList())) {return;}//隐含数据只有一条 PD 属性数据的情况,如果当仅有的一条数据不是//PD属性数据时,对后续逻辑存在在二次处理//这样会造成同一个逻辑分散到多个地方,代码零散逻辑不易理解if (batchRel.getBatchAttrList().size() == 1) {return;}Iterator<BatchAttr> lit = batchRel.getBatchAttrList().iterator();while (lit.hasNext()) {BatchAttr batchAttr = lit.next();if (!BatAttrMapping.PD.getKeyOmc().equals(batchAttr.getBatchKey())) {//修改入参数据造成逻辑执行上下文数据不一致,对于异常情况要保留原始数据时数据丢失//造成多处日志输出须要多处日志结合分析,增加日志排查难度//过多的相似日志增加日志量lit.remove();}}}//逻辑说明:只保留 PD 属性数据//优秀实现1private List<BatchAttr> getProductionDateAttr(PoBatchRel batchRel) {List<BatchAttr> batchAttrList = new ArrayList<>();if (Objects.nonNull(batchRel)&& CollectionUtils.isNotEmpty(batchRel.getBatchAttrList())) {for (BatchAttr batchAttr : batchRel.getBatchAttrList()) {if (BatAttrMapping.PD.getKey().equals(batchAttr.getBatchKey())) {batchAttrList.add(batchAttr);}}}return batchAttrList;}//优秀实现2private List<BatchAttr> getProductionDateAttr(PoBatchRel batchRel) {if (Objects.nonNull(batchRel)) {//保证不出现NullPointerExceptionList<BatchAttr> batchAttrs = Optional.ofNullable(batchRel.getBatchAttrList()).orElse(new ArrayList<>(0));return batchAttrs.stream().filter(o -> Objects.equals(BatAttrMapping.PD.getKeyOmc(), o.getBatchKey())).collect(Collectors.toList());}return new ArrayList<>(0);}
解析:
将相似逻辑可以聚合,以减少占用主逻辑篇篇幅。
实践建议:优先使用工具类提供的方法,经过多次验证的代码出现Bug的概率更低,将无必要的重复造轮子的时间专注与核心业务。
2.5 保持业务流程主体框架明了
1.主流程中只保留业务编排主逻辑;
2.将数据处理前后的数据处理工作(入参必填数据校验、入参转换内部业务对象、出参对象转换等)封装为独立的类或方法;
3.每个方法保持职责单一。
图1 逻辑主流程框架
阅读代码时能快速明确主逻辑,不被过多的细节所牵绊。
2.6 禁止项
1.禁止超千行代码类;
类文件超千行说明承载的职责不单一,应该通过合理的化分功能减少类文件代码行数。
2.禁止超百行代码块;
方法块超百行对于阅读代码时需要记忆的内容较多,增加阅读难度,同样也存在职责不单一问题。
3.禁止跨类调用通过修改入参对象内容返回结果的设计实现;
《编写高质量代码:改善Java程序的151个建议》 作者:秦小波
《大话设计模式》 作者:程杰
IntelliJ IDEA 插件 SonarLint
大道至简是个人认为代码整洁之道核心指导思想。保持业务流程主逻辑清晰明了,如骨架一般。细节实现部分使用现有工具减少大量低功能重复代码,再合理利用语言特性避免低级错误与相似重复逻辑。
面向对象编程要做到在不需要了解细节就能够快速明确业务主要逻辑,就要尽量将细节从主逻辑中隐去。将细节封装到各业务对象中。只对外提供高度抽象接口。如何使用JDK 所提供的Api一样。
以上是在工作中所见所遇,各系统都存在与示例中普通实现一样的逻辑,千行方法,万行类,重复造轮子等问题,造成系统代码臃肿,维护难不断增加。
求分享
求点赞
求在看