写点什么

代码重构,真的只有复杂化一条路吗?

  • 2022 年 5 月 20 日
  • 本文字数:2917 字

    阅读完需:约 10 分钟

本文分享自华为云社区《难道一开始就要把代码设计做得复杂吗?》,作者: JavaEdge 。


看着自己每次根据设计原则及模式的代码重构,虽然效果还不错,但你肯定也自省过:如果我的每段代码都这么写,是不是过度设计了?把握设计的度,确需长久锤炼。行业也总结了很多原则,帮助我们把握设计的度。它们是一种思考方法、一种行为准则。

KISS

Keep it simple, stupid,保持简单、愚蠢。提醒我们大多数系统,与其变得复杂,保持简单能让系统运行更好。


越资深的人,越觉得这大有道理。因为大佬们见识过因为复杂而引发的各种问题。堆太多功能,调整起来就很费劲。具体到一些场景:

  • 有现成库,就不自己写

  • 能用文本做协议,就别用二进制

  • 方法越短小精悍越好

  • 能把一个基本流程打通,软件就能发布,无需那么多功能(MVP)


看起来很接地气啊!真是吸引 crud boy 呢,但无法指导具体的工作。因为,啥叫保持简单,怎么就叫复杂?这都没有标准。


所以,有人基于自己的理解给了具体原则:

YAGNI

You aren’t gonna need it,你用不着它。如非必要,勿增功能。


软件设计对抗的是需求规模:

  • 通过努力,让软件在需求规模膨胀之后,依然能平稳发展

  • 努力控制需求规模


很多需求不需要做。很多产品经理以为很重要的功能实际上是没什么用的。真正重要的功能大约只占 20%,80%的功能可能大多数人都用不到。做了更多的功能,并不会得到更多的回报,但是,做了更多的功能,软件本身却会不断地膨胀,越发难以维护。


所以,在现实经常看到一些功能简单的东西不断涌现,去颠覆更复杂的东西。比如,虽然 Word 已经很强大了,但对于很多人而言,它还只是一个写字的工具,甚至它的重点排版功能都用得非常少。而 Markdown 简单地让我们专注写内容,而且简单的几个排版标记在日常沟通中就完全够用了。


尽量可能不去做不该做的事,从源头堵住问题吧!

DRY

Don’t repeat yourself,不要重复自己。在一个系统中,每一处知识都必须有单一、明确、权威地表述。Every piece of knowledge must have a single, unambiguous, authoritative representation within a system。


最简单理解:“不要做 cv 工程师”。这还远远不够,DRY 针对的是你对知识和意图的复制:在两个不同地方的两样东西表达形式不同,但表达内容却可能相同。


如下打印账户信息,这种写法肯定很常见:

public void printBalance(final Account account) {  System.out.printf("Debits: %10.2f\n", account.getDebits());  System.out.printf("Credits: %10.2f\n", account.getCredits());  if (account.getFees() < 0) {    System.out.printf("Fees: %10.2f-\n", -account.getFees());  } else {    System.out.printf("Fees: %10.2f\n", account.getFees());  }
System.out.printf(" ----\n");
if (account.getBalance() < 0) { System.out.printf("Balance: %10.2f-\n", -account.getBalance()); } else { System.out.printf("Balance: %10.2f\n", account.getBalance()); }}
复制代码

这段隐藏一些重复。比如,对负数的处理显然是复制的,可通过增加一个方法消除:

String formatValue(final double value) {  String result = String.format("%10.2f", Math.abs(value));  if (value < 0) {    return result + "-";  } else {    return result + " ";  }}
void printBalance(final Account account) { System.out.printf("Debits: %10.2f\n", account.getDebits()); System.out.printf("Credits: %10.2f\n", account.getCredits()); System.out.printf("Fees:%s\n", formatValue(account.getFees())); System.out.printf(" ----\n"); System.out.printf("Balance:%s\n", formatValue(account.getBalance()));}
复制代码

数字字段格式反复出现,不过,格式与我们抽取出来的方法是一致的,所以,可以复用一下:

String formatValue(final double value) {  String result = String.format("%10.2f", Math.abs(value));  if (value < 0) {    return result + "-";  } else {    return result + " ";  }}
void printBalance(final Account account) { System.out.printf("Debits: %s\n", formatValue(account.getDebits())); System.out.printf("Credits: %s\n", formatValue(account.getCredits())); System.out.printf("Fees:%s\n", formatValue(account.getFees())); System.out.printf(" ----\n"); System.out.printf("Balance:%s\n", formatValue(account.getBalance()));}
复制代码

打印格式其实也重复,如果我要在标签和金额之间加一个空格,相关的代码都要改,所以,这也是一个可以消除的重复:

String formatValue(final double value) {  String result = String.format("%10.2f", Math.abs(value));  if (value < 0) {    return result + "-";  } else {    return result + " ";  }}
void printLine(final String label, final String value) { System.out.printf("%-9s%s\n", label, value);}
void reportLine(final String label, final double value) { printLine(label + ":", formatValue(value));}
void printBalance(final Account account) { reportLine("Debits", account.getDebits()); reportLine("Credits", account.getCredits()); reportLine("Fees", account.getFees()); System.out.printf(" ----\n"); reportLine("Balance", account.getBalance());}
复制代码

重构后:

  • 改金额打印格式,就去改 formatValue

  • 改标签格式,就去改 reportLine


有人说这种调整粒度太小。如你这样感觉,证明你看问题的粒度太大。品味这个修改,与分离关注点和单一职责原则异曲同工:粒度要小。


DRY 不局限于写代码:

  • 注释和代码之间存在重复,可以尝试把代码写得更清晰

  • 内部 API 在不同的使用者之间存在重复,可以通过中立格式进行 API 的定义,然后用工具生成文档、模拟 API 等等

  • 开发人员之间做的事情存在重复,可以建立沟通机制降低重复;

  • ……


都是在试图减少重复,其实也是减少了维护成本。

简单设计

Simple Design,提出者 Kent Beck,只包含如下规则,后 3 条规则是重构方向

1 通过所有测试

保证系统能够按照预期工作。怎么知道系统按照预期工作,就需要有配套自动化测试,最好能 TDD,最根本的还是要懂设计,否则,你的代码就是不可测。

2 消除重复

正如 DRY 原则所说,你得能发现重复,就需要你对分离关注点有理解

3 表达出程序员的意图

编写有表达性的代码,这也需要你对“什么是有表达性的代码”有认识。代码要说明做什么,而不是怎么做

4 让类和方法的数量最小化

让类和方法的数量最小化,则告诉我们不要过度设计,除非你已经看到这个地方必须要做一个设计,比如,留下适当的扩展点,否则,就不要做。能做出过度设计的前提,是已经懂得了各种设计,这时才需要用简单设计的标准对自己进行约束。所以,所谓简单设计,对大多数人并不“简单”。


没有良好设计,代码就没有可测试的接口,根本没有办法测试,TDD 也就无从谈起。不懂设计,重构就只是简单提取方法,改改名字,对代码的改进也是相当有限的。


当然了,简单设计的前提是,把编程基础打牢。片面地追求敏捷实践,而忽视基本功,是舍本逐末。


点击关注,第一时间了解华为云新鲜技术~​

发布于: 刚刚阅读数: 2
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
代码重构,真的只有复杂化一条路吗?_代码_华为云开发者社区_InfoQ写作社区