首页 > 其他 > 详细

代码的味道:编写可复用代码的意识和技术

时间:2019-11-06 21:08:33      阅读:66      评论:0      收藏:0      [点我收藏+]

背景

有四个系统,A,B,C 均依赖 S 的服务。今天 CR 了两位同学的项目代码,均涉及 A,B,C 三个系统。发现:有一段“从 JSON 字符串中取特定字段的值”的相似功能在不同的系统多次出现,且两位同学都这么做了。

这样导致的问题是什么呢? 读者很容易想到了。相似的代码,会在不同的系统里多次出现;且每个同学为了取不同的字段,很可能都会写一段类似的代码。可想而知,这会有多少重复的工作量!多少重复的代码!重复的代码又会持续增加后续的维护成本。

原则与方法

事不过二

有个“事不过三”的原则:写第一遍,是因为新增的必须要写;写第二遍相似的代码,是因为两段逻辑可能有一点差异;写第三遍相似的代码,就不可接受了。

事实上,更严格的原则是:“事不过二”。当你发现要写第二遍时,就要意识到,这是重复的代码,需要将第一个抽离成更加通用的形式,方便复用。

公共模块

这里有两种场景。

如果相似的逻辑需要在同一个工程里反复使用,那就要在这个工程里添加一个工具类。对代码可维护性比较重视的开发者基本能做到这一点。如果不能做到这一点,先想办法做到这一点。

如果相似的逻辑需要在多个工程里反复使用呢?作为优秀的开发者,需要敏锐意识到这一点,并思考解决方案。

如果只是一段业务无关的代码,可以放在依赖工程的 API jar 里的工具类,上游的业务方都可以复用;如果是一段业务相关的代码,需要考虑开放一个通用的服务接口来提供。

对于上述情况而言,应该将这段取 json 串的字段值的逻辑放在 S 的 api jar 的工具类中。

通用形式

当打算提供一个够业务通用的工具类时,就需要考虑做到多通用的程度。 比如从 JSON 串中取指定字段的值:JSON 字符串是可变的,指定字段也是可变的。因此,必然存在两个入参。对于大部分业务场景,这样足够用了。少部分业务场景,由于可能存在嵌套的 JSON 串,可能要获取内层嵌套的字符串。

如果要做成一个通用的 JSON 字符串解析包,则需要考虑更多。比如在一个复杂嵌套的 JSON 串中,所要获取的字段可能在很深的层次。这时候,需要创建一种语法,来指明所取的字段位于 JSON 串中的位置。

通常,可以使用通用解析包来实现一个业务上够用的相对简单的通用工具类。

示例

比如有个 json 串如下。希望从中解析出指定字段的值。

{"IS_MEMBER":"true","PRICE":{"originAmount":4990,"totalAmount":4990},"BIZ_ORDER_EXTEND":"{\"CART_INFO\":\"fromRetail\"}"}

通常情况下,实现业务功能,并不需要非常通用的 json 解析能力。因此,我可以只提供两层的解析能力。比如定义两个基本函数 fetch 和 fetchString 。因为 String 出现频次非常高,因此提供一个函数用来获取字符串值。

public static Object fetch(String json, String field, String nestField) {
    return null;
  }

public static String fetchString(String json, String field) {
    Object obj = fetch(json, field, null);
    return obj == null ? "" : obj.toString();
  }

  public static String fetchString(String json, String field, String nestField) {
    Object obj = fetch(json, field, nestField);
    return obj == null ? "" : obj.toString();
  } 

现在,要实现 fetch 函数。可以采用 jsonpath 来实现。参见 “JsonPath:从多层嵌套Json中解析所需要的值”

public static Object fetch(String json, String field, String nestField) {
    try {
      if (StringUtils.isBlank(json) || StringUtils.isBlank(field)) {
        return null;
      }
      if (StringUtils.isBlank(nestField)) {
        return JsonPath.read(json, "$."+field);
      }
      return JsonPath.read(json, "$." +field + "."+nestField);
    } catch (Exception ex) {
      Object obj = JsonPath.read(json, "$."+field);
      if (obj instanceof String) {
        return JsonPath.read(JSON.toJSONString(JSON.parse(obj.toString())), "$." + nestField);
      }
      return null;
    }

  }

写个单测:

public class JsonUtilsTest {

  String json = "{\"IS_MEMBER\":\"true\",\"PRICE\":{\"originAmount\":4990,\"totalAmount\":4990},\"BIZ_ORDER_EXTEND\":\"{\\\"CART_INFO\\\":\\\"fromRetail\\\"}\"}";

  @Test
  public void testFetch() {
    Assert.assertEquals("", JsonUtils.fetchString(null, "IS_MEMBER"));
    Assert.assertEquals("", JsonUtils.fetchString(json, null));
    Assert.assertEquals("true", JsonUtils.fetchString(json, "IS_MEMBER"));
    Assert.assertEquals("4990", JsonUtils.fetchString(json, "PRICE", "totalAmount"));
    Assert.assertEquals("fromRetail", JsonUtils.fetchString(json, "BIZ_ORDER_EXTEND", "CART_INFO"));
  }
}


小结

代码的可复用性,是代码质量的一个重要的评定标准。程序员编写程序,并不仅仅是为了完成业务功能,还应该让自己的成果尽可能被其他人复用,产生更大的放大效果。

对于个人来说,可以增进对通用能力的技艺和领域;对于他人来说,则提升了他人的工作效率,扩散了自己的影响力。一举两得。

代码的味道:编写可复用代码的意识和技术

原文:https://www.cnblogs.com/lovesqcc/p/11808477.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!