复制粘贴、if判断---代码之丑

代码之丑

最近在极客时间APP上学习郑烨老师的经典编程课程"代码之丑",很有感触。

其实早在刚入行的时候就看过一点,但是那时觉得写的这些都是很常见的,不足为奇。可是随着自己工作年限的增长,过手的项目越来越多,代码量也越来越丰富。回过头来看这些文章,只能说这才是经典呀,那些在编程过程中只关注于业务的实现,却忘却如何更优美的表达,写出让后来者叹为观止的程序。

一个优秀的工程师能按时按要求完成需求的内容是基本的能力,但是能设计出鲁棒性,容错性,兼容性都兼顾的系统才是我们大家一直所要追求的。

最近拖更的时间有点长了,接下来我会不定期在此分享郑烨老师的这一系列文章,希望对大家能有帮助。

关于CVS 重复代码

对于一个新的需求,我们很多工程师都会选择在网上找对应的实现方式,本着不重复造轮子的理念,能用尽用的手法,先把功能实现了,后期再进行优化。

这样的做法没有问题,但是在做的过程中可能会出现一些不经意的遗留问题,比如直接拷贝已有代码,不认真学习分析代码细节,如果引用后不出现问题那一切都OK,但是若出现Bug,那么必将让你陷入难堪的境地。

复制粘贴最容易产生重复的代码,这块郑烨老师给的建议是:
不要使用复制粘贴,真正应该做的是,先提取出函数,然后,在需要的地方调用这个函数。

咱们看看下面这几段代码

@Task
public void sendBook() {
try {
this.service.sendBook();
} catch (Throwable t) {
this.notification.send(new SendFailure(t)));
throw t;
}
}
@Task
public void sendChapter() {
try {
this.service.sendChapter();
} catch (Throwable t) {
this.notification.send(new SendFailure(t)));
throw t;
}
}
@Task
public void startTranslation() {
try {
this.service.startTranslation();
} catch (Throwable t) {
this.notification.send(new SendFailure(t)));
throw t;
}
}

这三段代码表示的业务背景是,一个系统要把作品的相关信息发送给翻译引擎。比如发送作品信息,发送章节,启动翻译。

不难看出每个业务都不同,可是仔细看却又有几分相似,就是异常处理过程中,它会把出现异常的情况统一发送给某个人。

其实这块就存在重复的代码,如果说这种业务处理多的话,或者对于异常情况负责处理的人多了,那么修改起来是特别麻烦的。那么此时我们可以在此处进行优化。

从面向对象的设计来说,提取出一个接口。

private void executeTask(final Runnable runnable) {
try {
runnable.run();
} catch (Throwable t) {
this.notification.send(new SendFailure(t)));
throw t;
}
}

通过这个结构,前面几个方法就可以进行简化。

@Task
public void sendBook() {
executeTask(this.service::sendBook);
}

@Task
public void sendChapter() {
executeTask(this.service::sendChapter);
}

@Task
public void startTranslation() {
executeTask(this.service::startTranslation);
}

我们利用语言的特性,接口来实现了优化,这个时间看,这样子是不是简要多了,如果后期异常的处理人员增多,那么就只需求修改executeTask()方法即可。

关于代码中If的使用

在日常的业务编码过程中,经常会遇到对某种情况的判断,若满足是一种情况,不满足是另外一种情况。

if (user.isEditor()) {
  service.editChapter(chapterId, title, content, true);
} else {
  service.editChapter(chapterId, title, content, false);
}

这是对一段章节内容的审核,若审核通过之后,做后续的处理。但是调用时必须看是否为编辑者。

这段代码当时写的时候,作者的脑子里应该只想到了if语句判断之后要做什么,而没有想到这个if语句判断的到底是什么。

仔细看if判断完之后的内容调用的方法几乎都是一样的,只是最后的参数不同而已。

按照一般的逻辑我们是不是直接可以写成这样子。

service.editChapter(chapterId, title, content, user.isEditor());

由于此方法只是最后的Boolean参数发生了变化,那么直接给将引起变化的值直接赋值即可。

哈哈哈,我也是这么想的,这样子代码量也可以减少,而且不必做更多的判断。这么做没有问题,但是仔细看这种写法代码的可读性很差,如果想了解这个方法的参数那么必须得调试进行才可以,而且这种写法不方便调试。

大家来看郑烨老师的写法。

boolean approved = user.isEditor();
service.editChapter(chapterId, title, content, approved);

如果说审核的条件发生了变化,那么此时还可以提取出一个函数,把可能的变化都集中在这个函数中。

boolean approved = isApproved(user);
service.editChapter(chapterId, title, content, approved);


private boolean isApproved(final User user) {
return user.isEditor();
}

大家看是不是可读性提高了很多,从上到下阅读起来很顺畅。

所以我们要注意:只要在代码中看到了if语句的出现,而且if和else的代码块长得又比较像,多半就是出现了这个坏味道。

知识点回顾

复制粘贴的代码、结构重复的代码、if和else代码块中的语句高度类似。

Don't Repeat Yourself(不要重复自己,简称 DRY)

复制粘贴的代码和结构重复的代码,虽然从观感上有所差异,但本质上都是重复。

作为一个精进中的程序员,我们一定要把DRY原则牢记在心,时时刻刻保持对“重复”的敏感度,把各种重复降到最低。

寄语

人生短暂,我不想去追求自己看不见的,我只想抓住我能看得见的。

原创不易,给个关注。

技术群:添加小编微信并备注进群

小编微信:mm1552923   

公众号:dotNet编程大全    


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值