恕我直言,你不懂开发

我发现,多数同学,即使有几年的开发经验,但在应用开发中,思考的比较少。开发一个功能其实是比较容易的,大家都能完成,不过,如果缺乏必要的思考,上来就是干,那么,往往编写的代码要么不易读,要么耦合度高,要么代码结构混乱,要么扩展性不足。这样的代码一多,系统就成了所谓的烂系统了。带来的恶果就是可维护性差,维护成本高,这无疑会大大降低团队的开发效率。
我看过许多这样的缺乏思考、缺乏设计的代码,阅读这样的代码有时会感到啼笑皆非。
☘ 为何放着简单不用?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
- 重命名静态的
getNext2
为getNext
☘ 使用 对象强转 还是 bean拷贝?
下面代码中,Result是一个泛型类 Result<T>
。rpc服务 channelLevyService.getById 返回 Result
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所说:"任何一个傻瓜都能写出计算机可以理解的代码,唯有能写出人类容易理解的代码的,才是优秀的程序员。"与诸君共勉。
当看到一些不好的代码时,会发现我还算优秀;当看到优秀的代码时,也才意识到持续学习的重要!--buguge
本文来自博客园,转载请注明原文链接:https://www.cnblogs.com/buguge/p/18763748