buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

恕我直言,你不懂开发

我发现,多数同学,即使有几年的开发经验,但在应用开发中,思考的比较少。开发一个功能其实是比较容易的,大家都能完成,不过,如果缺乏必要的思考,上来就是干,那么,往往编写的代码要么不易读,要么耦合度高,要么代码结构混乱,要么扩展性不足。这样的代码一多,系统就成了所谓的烂系统了。带来的恶果就是可维护性差,维护成本高,这无疑会大大降低团队的开发效率。

我看过许多这样的缺乏思考、缺乏设计的代码,阅读这样的代码有时会感到啼笑皆非。



☘ 为何放着简单不用?Money 类使用中的低效 “绕路” 行为

系统公共包中有个Money类。Money类包括 fen() 和 yuan() 两个属性方法。

然后,程序里有下面这样的代码,其中 recharge.getTotalAmount() 返回的是 Money。

saleRechargeVO.setTotalAmount(BigDecimalUtil.convertFen2Yuan(recharge.getTotalAmount().fen()));

尼玛,真是亮瞎了我的24K钛合金狗眼。这代码可真能绕:①获取Money.fen ② 分转元

你直接获取 Money.yuan(),不好吗?

saleRechargeVO.setTotalAmount(recharge.getTotalAmount().yuan());

☘ 逻辑代码中使用工具方法混乱

泛型类 Result<T> 中有一个 getData() 方法,与之对应的还有下面的 getDataSafe() 方法,从方法实现可知,当 非success 时,方法会 throw 异常。

public class Result<T> implements Serializable {
    private Integer code = 0;
    private String message = "操作成功!";
    @JSONField(serializeUsing = StringAbbreviatingSerializer.class)
    private T data;


    public T getData() {
        return this.data;
    }

    @Transient
    @JSONField(serialize = false)
    public T getDataSafe() throws BizException {

        if (this.isSuccess()) {
            return this.data;
        }
        throw new BizException(this.getMessage());
    }

    ....
}

下面是一个开发者在程序中调用RPC接口的代码。

Result<List<SomeDTO>> result = someRpcApi.list(query);
List<SomeDTO> list = result.getDataSafe();
for (SomeDTO one : list) {
    ...
}

而后,有同学在评审这段代码时提出意见,再次强调了 result.getDataSafe() 可能会抛异常。这位开发者迟疑了一会儿,将代码改成了下面的样子。很显然,他没有明白到底应该怎么改。

Result<List<SomeDTO>> result = someRpcApi.list(query);
if (result.getDataSafe() != null) {
    List<SomeDTO> list = result.getDataSafe();
    for (SomeDTO one : list) {
        ...
    }
}

正确的姿势,也许是下面这样:

Result<List<SomeDTO>> result = someRpcApi.list(query);
if (result.isSuccess()) {
    List<SomeDTO> list = result.getDataSafe();//或直接调用 getData()
    for (SomeDTO one : list) {
        ...
    }
}

☘ 使用Result实例来给另一个 new的Result赋值。

我们系统的Result<T>中有 code/msg/data,还有静态工厂方法,例如

  • Result<T> success()
  • Result<T> success(T data)
  • Result<T> success(T data, String msg)
  • Result<T> successWithMsg(String msg)
  • Result<T> error(String msg)

我在走查代码时,发现这样的代码,

Result<Invioce> result = new Result<>();
boolean exists = invoiceRepository.exists(businessNo);
if (!exists) {
  invoiceRepository.saveData(entity);
  ...
  result.setCode(Result.success().getCode());
  result.setData(entity);
} else {
  ...
  result.setCode(Result.error().getCode());
  result.setMsg("数据已存在");
}
return result;

这段代码坏味道十足。改成下面会优雅许多。

boolean exists = invoiceRepository.exists(businessNo);
if (!exists) {
  invoiceRepository.saveData(entity);
  ...
  return Result.success(entity);
} else {
  ...
  return Result.error("数据已存在");
}

裤裆着火 —— 当然了 (裆燃了) ,单说Result<T>,既然已经有了静态工厂方法来让调用者构造对象,就不应该暴露构造器方法了,这样才彻底断了开发者直接new Result<T> 的念想。

☘ Util类里重复的方法,开发者重构后,让人啼笑皆非

下图中重复定义的两个方法,一个是静态的 getNext,一个是非静态的 getNext2 ,存在于 一个id或单号生成工具类 IdNoGenUtil 中。我截图发给开发者调整一下。

然后,开发者是按下面两点调整的:

  • 相关调用 getNext2 的改为 调用 getNext
  • 删掉静态的 getNext2

姿势恰好反了!

正确的姿势是按下面的调整:

  • 相关调用 getNext 的改为 调用 getNext2
  • 删掉非静态的 getNext
  • 重命名静态的 getNext2getNext

☘ 使用 对象强转 还是 bean拷贝?

下面代码中,Result是一个泛型类 Result<T>。rpc服务 channelLevyService.getById 返回 Result。BeanMapper.map 用来处理JavaBean转换。

Result channelResult = channelLevyService.getById(vo.getProviderId());
ChannelLevyDTO channelDto = BeanMapper.map(channelResult.getResult(), ChannelLevyDTO.class);

上面代码,显然存在问题。正确的姿势是

Result<ChannelLevyDTO> channelResult = channelLevyService.getById(vo.getProviderId());
ChannelLevyDTO channelDto = channelResult.getResult();

当然,存在即合理(事物既然存在,即尤其合理的一面)。我们得分析一下这段代码之所以存在的来历。
曾经,rpc服务 channelLevyService.getById 返回类型是 未指定T的Result————尽管这个方法里返回的实际的T是ChannelLevyDTO。
然后,开发者在调用这个rpc接口时为了获取到 Result 中实际的数据,就用了 BeanMapper.map。
开发者显然缺乏对java技术的娴熟运用,他当时应该这么写:

Result channelResult = channelLevyService.getById(vo.getProviderId());
ChannelLevyDTO channelDto = (ChannelLevyDTO)channelResult.getResult();



Martin Fowler:"任何一个傻瓜都能写出计算机可以理解的代码,唯有能写出人类容易理解的代码的,才是优秀的程序员。"

从上面的几个典型案例中,我们可以深刻感受到代码质量问题的普遍性和危害性。这些"啼笑皆非"的代码背后,反映的是开发者对编程思想、设计原则和工程规范的认知缺失。

《重构》作者Martin Fowler所说:"任何一个傻瓜都能写出计算机可以理解的代码,唯有能写出人类容易理解的代码的,才是优秀的程序员。"与诸君共勉。

posted on 2025-03-30 22:45  buguge  阅读(25)  评论(0)    收藏  举报