你会拆分这样的函数么?

2014-12-05 16:16:51 +08:00
 spacewander
有一个函数,是这样子的:
(仅仅用作表示,真实函数肯定不叫doSomething)

doSomething(a, b, c)
{
// prepare
...
// do A
...
// do B
...
// do C
...
}

前提:
1. do A/B/C中的内容不会被其他函数复用,仅仅在doSomething中被用到
2. 这是C++写的
3. 该函数不是“厨房下水道”函数,其长度是300多行

有人认为,应该把do A/B/C部分,分别拆开成单独的函数,然后让doSomething调用它们。
理由:
1. 这样做大大减少了doSomething函数的大小,使得整个函数更加一目了然
2. 无需再添加一行注释表示该部分代码是做什么的,用函数名就能清晰表示它们的职责
3. 将部分逻辑置于独立的函数之中,便于调试

不过楼主不认同这个观点,反而认为维持现状即可。
理由:
1. 这些部分不会被复用,即使独立成单独的函数,意义不大,不应该过度设计。
2. 独立成单独的函数后,需要将函数的输入/输出参数重复一遍,而且万一以后发生变动,不得不修改多个函数之间的传递的类型信息。
3. 独立出更多的函数后,看上去程序更好理解了,但实际是把单个函数的维护成本分摊到多个子函数上了。如果这样做了,要想明白doSomething的实现细节,你就不得不去阅读4个函数:doSomething,doA,doB和doC。
4. 一旦doSomething的逻辑发生较大变化,原本只需修改doSomething一个函数,现在则需要修改三个子函数。分拆函数就是为了把逻辑独立出去,但是这三个函数在逻辑上不一定能够独立开来,分拆它们会把事情变得更复杂。

各位怎么看?
6307 次点击
所在节点    程序员
44 条回复
goool
2014-12-05 17:16:01 +08:00
程序里偶尔出现长函数是可以理解的,参数检查、资源申请、错误处理、对象转换等等,七七八八“不干正事”但又不能没有的周边代码加起来自然就长了。

但是——书上说的总是对的:别写长函数;但是,但是——实际代码总是有妥协的。

总的来说,别真的太长,长函数别太多,一两个没事,再多就有问题了。
acros
2014-12-05 17:19:40 +08:00
我很可能会拆。
问题在第一句: “do A/B/C中的内容不会被其他函数复用,仅仅在doSomething中被用到”
这个情况我不太敢肯定。需求永远会变的,即使看起来只被一个地方调用的函数,实际都有很高的概率被复用,先做好这个API比较容易被别人引用。如果写在一个大函数里面,可能自己都想不起来了。
Sunyanzi
2014-12-05 17:20:52 +08:00
不支持拆 ... 具体参考我之前的回复 /t/150282#reply12 ...

结构化的部分使用合理的缩进区分 ...
Her0
2014-12-05 17:21:06 +08:00
我不会拆。
第一:楼主明确说了拆出来的ABC也没有其他函数会调用它们,所以拆了冗余
第二:拆出来需要每个函数都要重新写一遍参数,万一以后要改,代价就大了
ipconfiger
2014-12-05 17:26:08 +08:00
@acros 不太感肯定的时候就先拆了再说是典型的过度设计的表现,首先你怎么知道你的拆分就是完全正确的?第二万一拆分后以后要复用的点不一样,那么你要怎么改?
借用楼上很多主张拆的同学说的观点,有重构工具了拆分函数很方便,yes,这就是不拆的理由,将来万一需要拆分,有重构工具也就是很简单就可以拆出去了。

现代IDE有folding功能可以把大函数的几个功能块折叠了,看不是问题

再次重申,提前优化是万恶之源
anerevol
2014-12-05 18:17:39 +08:00
通常情况我是拆的,除非ABC相关非常密切。
忽然忘记300行是啥概念了,翻了翻自己的代码通常一个类200~400行。
dorentus
2014-12-05 18:30:01 +08:00
如果没有其他原因的话,我不会选择去动这个函数。
deljuven
2014-12-05 18:39:04 +08:00
各个部分加注释加log,然后不动。。。
spacewander
2014-12-05 18:42:08 +08:00
@binux
你说得也对,拆分之后适合更加细化去考虑每个子函数的职责。
有一点我没有提出来……doA和doB和doC间传递(依赖)的参数过多,有4个deque和3个map。这样的参数依赖使得复用是基本不可能的……而且万一要从deque改成list,或者map改成unordered_map,函数参数就要大改了。
而且现在不拆分,除了相对难以理解,并不会带来什么问题。而即使拆分了,也不会带来什么好处,因为从业务逻辑或者代码间的依赖而言,复用是无需考虑的事。等到将来有需要,再进行拆分,也不算迟。
williamx
2014-12-05 18:45:27 +08:00
对 lz 来说,这其实是一个非常主观的问题,而且自己其实已经有答案了,别人再多说什么也没有意义。我能说的只剩下:
如果是我来写的话,看着不爽就拆,而不是考虑是不是被复用!
spacewander
2014-12-05 18:58:08 +08:00
@binux 你说得对,分拆成各个函数后可以专注于每个函数的职责。
我忘了提到一点……doA/doB/doC之间依赖非常重,是4个deque和3个map。
第一个获取数据,填充这几个容器
第二个根据前面获取的数据,再进行粗加工
最后一个根据前面两步的结果再进行细加工
因为相关逻辑的关系,基本上复用不会有的……就算将来要复用,再分拆也不迟。

总之,现在不拆,麻烦就是程序逻辑相对难懂,但是拆了,就变得不那么灵活了。(这三个之间的耦合太重了,拆开比较困难)

另外,如果要把deque改成list,把map改成unordered_map,相关的函数参数上的修改会令人抓狂的,而如果不拆,工作量会小很多。
spacewander
2014-12-05 19:01:27 +08:00
@repus911
其实debug(无论是自己还是将来的那谁)的时候,还是得细看源码。
另外既然你提到了Python了,我只想说“动态语言大法好”!
spacewander
2014-12-05 19:04:59 +08:00
网络问题……我还以为第一次回复不成功了呢……
layout
2014-12-05 19:05:06 +08:00
支持拆分:
1.函数过长维护难度变大,有可能变量定义在函数开头,底部还在使用,出错几率增加,同时阅读难度增加;
2.职责划分,这三块虽然只在一个函数中被调用,但是可以将职责相当的内容封装在一起,便于理解和改动;现在不调用不代表将来不调用;
3.便于测试,针对一个300行的函数写单元测试....我会奔溃,但是针对3个100行的函数,测试要考虑的点会少一些;

这个世界是有很多个简单组成的复杂体,而不是一个大而冗长的对象。任何东西大了就会分解成为小的,便于我们的理解和重用。这也是为什么出现了组件化、对象化、函数化等设计方法。
icylogic
2014-12-05 19:22:10 +08:00
就算题主已经有预设立场也不妨碍我们继续讨论嘛

我目前觉得拆分函数, 主要就两个目的

一个是重构成正交函数, 从而方便复用和增强可读性. 如果你觉得这是一段线性的, 连续的逻辑, 而不存在分支或者更复杂结构的逻辑, 那这个理由就没有了

二是为了测试而调整, 我是这样想的, 比如一个流程是 A->B->C, 各有5种可能的输入会产生 bug, 为了让这些情况都被覆盖到, 你**可能**就要设计一大坨测试用例(取决于ABC之间的关系, A 也许永远不会产生会导致 B 出现 bug 的输出, 那就只需要照顾到 A, 但这是隐患, 如果你将来不记得了, 把 A 改成 A', 可能就会出问题), 但是拆成ABC, 那你每个测试都写很少的用例就可以覆盖到全部情况了
luoweihua7sync
2014-12-05 20:44:22 +08:00
同意二楼,单一原则,应该拆分。
我个人认为,代码有2个主要作用:
1,运行,不能跑的代码什么都不是
2,可读(可维护)。
ryd994
2014-12-05 20:55:30 +08:00
如果确认不可能复用,那不拆也可,但是用多行空行隔开,还有注释,保持清晰。
chmlai
2014-12-05 22:51:03 +08:00
分开来写成方法不一定非为了复用, 很多时候是分离以下复杂度.
ariestiger
2014-12-05 23:06:40 +08:00
当然得拆了。
尽量减少方法行数,超过 50 行的方法, 如果不是多个循环嵌套或者 if 来 else 去, 那说明这方法里已经干了不少事了, 方法体短一点, 方法名参数名有意义一点,节省别人的思考时间, 也让将来自己回头看的时候脑子少转个弯。
为什么设计,开发,面向用户的时候都说“不要让我思考”, 等到写代码需要和同事交流,需要和未来的自己交流的时候, 就不这么考虑呢?
JamesRuan
2014-12-06 03:58:57 +08:00
拆!300行一个函数不能忍!
函数不仅仅是为了复用而写的,更是为了合理划分功能,提高可读性和可测试性。
一个几百行的函数,十多个只用一两次的局部变量好,还是几个几十行的函数,每个里个位数的局部变量好?
可读性,可测试性,可维护性哪个都是后者高。
损失呢?除了内存使用和调用时间都是O(1),增加的代价可以忽略不计。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/151763

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX