为了方便大家看视频,我已经把全部四集(已完结)传到了土豆上面,你懂的。整个四集的时间大概在两个半小时。
——————————— 建议你先看完视频再看我下面的评论———————————
我在往来上海,北京和福州的飞机上面看完了所有四集。Justin 的英文说的比较快,有时挺含糊的,有些地方没太听懂。好在有代码演示,应该使我能够理解他的做法和理念了。第一集开始讲的是他对 Detroit School TDD 和 London School TDD 的理解,这部分我很认同,总结的很到位。然后他提出了自己根据 London School TDD 发展出来的 Discovery Test 方法,瞬间让我很期待后面的三集。不过可惜的是,看完之后的感觉离我的预期差距较大,我无法认同 Discovery Test 这种做法。
总体来说,我认为 Discovery Test 并没有很好的继承 London School TDD 的做法,甚至产生了背离。另一方面,整个过程有明显的过度设计。具体来说:
- 整个过程中 Justin 并没有写验收测试,以至于我无法理解他要实现的需求是什么。比如,他在讨论设计的时候提到 KeepsTime 和 TimeLimit 这样的概念,我就不明白到底是什么需求导致了这样的设计。说到底,如果没有明确的需求,我完全可以认为 Game Of Life (GOF)和时间的关系很小。比如,GOF 可以无限制的把 Cell 变换下去,每隔 0.1 秒变一次就可以了。
- GOOS 方法中的验收测试,等同实例化需求中的例子,是对业务需求的最小拆分并拥有用户价值,让开发过程始终有一个业务聚焦点。夸张点说,程序员在写代码和测试的每一秒都应该思考的一个问题,就是“我如何让验收测试尽快通过,尽早交付业务价值”。
- Justin 甚至都没有提到验收测试这件事,直接导致他在后面三集的开发中,对设计进行不少“YY”(我在下面会提到)。他在第一集的最后给出过别人实现的带界面的 GOF,但是他自己的代码到最后也没有实现那种效果。
- 如果是我来开发的话,我会先写一个验收测试,比如,在网页上面显示一个 1*1 的矩阵,里面只有一个代表 dead cell 的点(白色),经过 0.1 秒之后,还是显示这样的矩阵。虽说把矩阵显示出来并不是开发的风险所在(Justin 也这么说),但是我在实际工作中遇到过太多以为没有风险的地方最后却发生了风险的事情。写一个简单的验收测试,或者至少手动验收一下,可以很好的避免程序员喜欢“YY”的毛病,减少开发风险。
- 由于没有验收测试的缘故,Justin 在他的 Design Session 里面提到了很多概念,并且在之后的演示中把这些概念一个不差的变成了代码。这里面有明显的过渡设计,具体表现如下:
- KeepsTime 和 TimeLimit 为什么需要?至少这两类到最后也没有实际的代码。
- 为什么有了 World,还要一个 MutableWorld?我感觉有 World 这个类就可以了。Justin 的解释是 GenerateSeedWorld 产生的 World 有可能对 at(Coordinates) 的实现很不相同,但是我觉得他在没有证实这一点之前就引入 MutableWorld 就太早了
- 总的来说,我觉得 Coordinates, Point, Contents, Outcome 都可以被 Cell 代替
- 为什么需要 Contents?我觉得 Contents 可以被 Coordinates/Point 这个类代替,同时给他增加一个 neighbours 方法就可以了(其实这个类应该改名叫 Cell 更合适)。到最后,Contents 里面都没有代码
- 为什么需要 Outcome?我觉得 ReplacesCell 的 replace 方法直接返回一个下一状态的 Point (或者说 Cell)就可以了。到最后, Outcome 里面只有 Contents 和 neighbours 这两个数据
- DeterminesNextContents 和 GathersNeighbors 的代码应该被融合进 ReplacesCell,这些本来就是 ReplacesCell 应该做的事情。GathersNeighbors 没有代码实现,这个类不就是在做 Cell.neighbours 的事情吗?
- 因为这些过度设计,导致 Justin 的代码中有很多 Middle Man 的代码臭味
- 整个四集中,Justin 很少花时间重构代码,我觉得并不合理。说实话,测试代码中有不少重复是可以去掉的。他在第一集里面提到过 GOOS 方法要比 Detroit School TDD 做重构的时间要少一点,这点我认同。不过,GOOS 虽然重构少一点,但绝对不是没有,而且关键时刻还是会推翻现有设计的。大家看了 GOOS 这本书就会明白。
- 有人认为 GOOS 方法强调依赖倒置原则(DIP),所以称其为 Mockist TDD。我觉得这是一种误解,其实 GOOS 首先强调的是单一职责原则(SRP)。因为在 GOOS 中,每当你写一个类的UT时,就应该思考他的职责是什么,那么那些不是他的职责范围的代码就自然变成了依赖(在UT中被隔离)。Justin 其实也是这么做的,只不过因为没有一个体现需求的验收测试,也没有从最小的需求入手,所以导致了过度设计。
不知不觉写了这么多,我也是醉了。最后预告一下,经过这一年在开发和 coaching 中对 GOOS 的应用,我发现他对提升代码质量和开发团队设计编码能力非常有帮助。我计划在明年更大范围的推广 GOOS,并尝试做些社区活动或者课程来推广他。
谢谢,
Joseph