我开始编写单元测试的次数越多,我就越发现自己编写越来越小的类。这些类现在很小,因为它们中的许多只有一个与接口绑定的公共方法。然后测试直接针对该公共方法并且相当小(有时公共方法将调用类中的内部私有方法)。然后我使用IOC容器来管理这些轻量级类的实例化,因为它们有很多。
这是尝试以更多TDD方式做事的典型吗?我担心我现在已经重构了一个遗留的3,000行类,其中有一种方法,它在频谱的另一面也难以维护,因为现在有大约100个不同的类文件。
我正在做的事情走得太远了吗?我试图用这种方法遵循单一责任原则,但我可能正在进入一种贫穷的阶级结构,我没有非常聪明的“业务对象”。
答案 0 :(得分:2)
这么多小班会让我疯狂。有了这种设计风格,很难弄清楚真正的工作在哪里完成。我也不喜欢拥有大量接口,每个接口都有相应的实现类。在我的书中有很多“IWidget”和“WidgetImpl”配对是代码味道。
将3000线级别分成更小的部分非常棒且值得称道。但请记住目标:使代码更易于阅读并更易于使用。如果您最终得到30个类和接口,您可能只是创建了一个不同类型的怪物。现在你有一个非常复杂的课程设计。需要花费大量精力才能将许多课程直接放在脑海中。有了很多小班,你就失去了打开几个关键文件的非常有用的能力,找出最重要的方法,并了解到底发生了什么。
但是,对于它的价值,我并不是真的以测试驱动的设计出售。尽早写测试,这是明智的。但重新组织和重组您的课程设计,以便更容易进行单元测试?不用了,谢谢。我只会在具有架构意义的情况下创建接口,而不是因为我需要能够模拟一些对象以便我可以测试我的类。那是把车推到马前。
答案 1 :(得分:1)
如果你问这个问题,你可能会走得太远。在类中只有一个公共方法并不坏,如果该类具有明确的职责/功能并封装了与该函数相关的所有逻辑,即使其中大多数是私有方法。
在重构这样的遗留代码时,我通常会尝试在高级别中识别可以分配不同角色/职责的组件,并将它们分成各自的类。我考虑哪些功能应该由哪些组件负责,并将方法移动到该类中。
答案 2 :(得分:0)
您编写了一个类,以便该类的实例保持状态。你把这个状态放在一个类中,因为类中的所有状态都是相关的。你有管理这个状态的功能,所以不能设置无效的状态排列(臭名昭着的广场有成员宽度和高度,但如果宽度没有高度等于它不是真正的正方形。)
如果你没有状态,你不需要一个类,你可以只使用自由函数(或者用Java,静态函数)。
所以,问题不是“我应该有一个功能吗?”而是“我的班级封装了什么有形的实体?”
也许你有一个设置所有状态的函数 - 你应该让它更精细,所以,例如,你应该有一个void Rectangle::setWidthAndHeight( int x, int y)
而不是setWidth
而不是setHeight
}}
也许你有一个设置的ctor,以及doesIt
,无论它是什么的单个函数。然后你有一个仿函数,一个doIt
可能有意义。例如,class Add implements Operation { Add( int howmuch); Operand doIt(Operand rhs);}
(但是你可能会发现你真的想要像访客模式这样的东西 - 如果你有纯粹的价值对象,那么纯粹的仿函数就更有可能,如果他们被排列在一棵树中并且彼此相关,那么访客就更有可能。)
即使拥有这么多小对象,单一功能也是正确的粒度级别,您可能需要像Facade Pattern这样的东西来构成原始操作,通常使用的复杂操作。
没有人回答。如果你真的有一堆仿函数,那很酷。如果你真的只是让每个自由函数成为一个类,那就是愚蠢的。
真正的答案在于回答这个问题,“我管理的是什么状态,以及我的课程对我的问题领域的建模程度如何?”
答案 3 :(得分:0)
我会猜测我是否在不查看代码的情况下给出了明确的答案。
然而,这听起来像你关心,这是审查代码的明确标志。您问题的简短回答可以追溯到definition of Simple Design。最少数量的类和方法就是其中之一。如果您觉得可以在不丢失其他所需属性的情况下删除某些元素,请继续折叠/内联它们。
一些帮助您决定的指示: