过度设计有意义吗

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

1 过度设计有意义吗?

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


2 KISS

Keep it simple, stupid,保持简单、愚蠢。提醒我们大多数系统,与其变得复杂,保持简单能让系统运行更好。越资深的人,越觉得这大有道理。因为大佬们见识过因为复杂而引发的各种问题。堆太多功能,调整起来就很费劲:


有现成库,就不自己写

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

方法越短小精悍越好

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

真是吸引crud boy,但无法指导具体工作。啥叫保持简单,怎么就叫复杂?这都没标准。有人基于自己的理解给具体原则:


3 YAGNI

You aren’t gonna need it,你用不着它。如非必要,勿增功能。软件设计对抗的是需求规模:


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

努力控制需求规模

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


所以,在现实经常看到一些功能简单的东西不断涌现,去颠覆更复杂东西。如Word强大,但只是个写字工具,重点排版功能都用得少。而Markdown简单地让我们专注写内容,而且简单的几个排版标记在日常沟通中就完全够用。尽量可能不去做不该做的事,从源头堵住问题。


4 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 等等

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

……

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


5 简单设计

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


1 通过所有测试

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


2 消除重复

正如DRY,你得能发现重复,就要会分离关注点


3 表达出程序员的意图

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


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

让类和方法的数量最小化,不要过度设计,除非你已看到这必须要做个设计,比如,留下适当扩展点,否则,就不要做。

能做出过度设计的前提,是已懂得各种设计,这时才需要用简单设计的标准对自己约束。所谓简单设计,对大多数人并不“简单”。


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


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


文章知识点与官方知识档案匹配,可进一步学习相关知识

目录
相关文章
|
7月前
|
缓存
代码优化与过度设计:寻找平衡的艺术
作为开发人员,我们常常会面临一个棘手的问题,即如何在代码优化和过度设计之间找到平衡点?因为我们都希望通过优化代码来提升程序性能,但实际情况是稍有不慎就可能陷入过度设计的泥潭,让代码变得难以理解和维护,反而适得其反。在实际开发中,我们应该如何在这两者之间找到平衡呢?那么本文就来简单分享一些经验和方法,从而帮助我们避免陷入这种困境泥潭中。
107 3
代码优化与过度设计:寻找平衡的艺术
|
7月前
|
测试技术 开发者
开发认为过度测试了该怎么办?
开发认为过度测试了该怎么办?
开发认为过度测试了该怎么办?
|
7月前
|
设计模式 IDE Java
谈谈过度设计:因噎废食的陷阱
本文探讨了设计模式在软件开发中的应用和争议,指出设计模式虽有助于应对软件复杂性,但在互联网快速迭代的背景下,可能会导致过度设计,增加理解和修改成本。文章分析了设计模式的缺陷,如开闭原则可能导致不易修改,最小知识原则可能导致理解困难。同时,文章强调了设计模式的重要性,指出它们可以提高代码的可理解性和模块的可维护性,并提出了通过函数式设计模式进行优化的示例。作者认为,设计模式需要随着业务演进而不断演进,同时提倡使用可调试的模块和模式演进来促进系统的成长性。文章最后提醒读者,要根据实际情况选择是否使用设计模式,避免因噎废食。
|
7月前
|
设计模式 算法
我确实遇到过优化代码却导致过度设计的状况
我确实遇到过优化代码却导致过度设计的状况
48 10
|
7月前
|
缓存 编译器 Go
反射的双刃剑:性能与灵活性权衡
反射的双刃剑:性能与灵活性权衡
64 0
|
7月前
|
开发者
浅谈代码优化与过度设计
浅谈代码优化与过度设计
|
设计模式 机器学习/深度学习 算法
聊一聊过度设计!
新手程序员在做设计时,因为缺乏经验,很容易写出欠设计的代码,但有一些经验的程序员,尤其是在刚学习过设计模式之后,很容易写出过度设计的代码,而这种代码比新手程序员的代码更可怕,过度设计的代码不仅写出来时的成本很高,后续维护的成本也高。因为相对于毫无设计的代码,过度设计的代码有比较高的理解成本。说这么多,到底什么是过度设计?
267 0
|
搜索推荐 SEO
出现这些情况说明是网站过度优化
出现这些情况说明是网站过度优化
91 0