2010-07-16 94 views
4

我越是写入单元测试越多,我发现自己写越来越小的类。这些类现在很小,因此它们中的许多只有一个公共方法,它们与接口相关联。然后,测试直接针对该公共方法,并且相当小(有时该公共方法将调用该类内部的私有方法)。然后我使用IOC容器来管理这些轻量级类的实例化,因为它们有很多。编写通常只有一种公开方法公开的类是否是一种很好的做法?

这是典型的尝试以更多的TDD方式做事吗?我担心现在我已经重构了一个传统的3000线类,其中有一种方法,因为现在实际上大约有100个不同的类文件,这些类在另一方面也很难维护。

我在做什么太过分了?我试图用这种方法来遵循单一职责原则,但我可能会陷入一种贫乏的阶级结构,我没有非常聪明的“业务对象”。

回答

2

这个众多的小班会让我疯狂。有了这种设计风格,真正很难弄清真正的工作在哪里完成。我不是拥有大量每个具​​有相应实现类的接口的粉丝。有很多“IWidget”和“WidgetImpl”配对是我书中的代码味道。

将3000条线条分成小块是非常值得赞扬的。不过要记住的目标是:使代码更易于阅读和更容易使用。如果你最终得到30个职业和接口,你可能只是创造了一种不同类型的怪物。现在你有一个非常复杂的类设计。需要花费很多精力才能让这些课程顺利进行。而且有很多小班,你失去了打开几个关键文件的非常有用的能力,选择最重要的方法,并了解到底发生了什么。

尽管我不太喜欢测试驱动设计,尽早编写测试,这很明智。但重新组织和重构你的课堂设计,以便更容易地进行单元测试?不用了,谢谢。我只会在构建接口的时候才使用接口,而不是因为我需要模拟一些对象,以便测试我的类。这是把马车放在马前。

1

如果你问这个问题,你可能会走得太远。在类中只有一个公共方法并不坏,因为如果该类具有明确的责任/功能并封装了与该函数有关的所有逻辑,即使其中大部分都是私有方法。

当重构这样的遗留代码时,我通常会尝试识别高层次的组件,这些组件可以分配不同的角色/职责并将它们分离到他们自己的类中。我认为哪些功能应该是哪些组件的责任,并将这些方法转移到该类中。

0

您编写了一个类,以便该类的实例保持状态。你把这个状态放在一个类中,因为类中的所有状态都是相关的。你有函数来管理这个状态,这样就无法设置状态的无效排列(臭名昭着的正方形有成员宽度和高度,但是如果宽度不不等于它不是一个真正的方形。)

如果你没有状态,你不需要一个类,你可以只使用免费函数(或在Java中,静态函数)。

所以,问题不是“我应该有一个功能吗?”而是“我的班级封装的是什么有状态的实体?“

也许你有一个函数,将所有国家 - 和你应该让更精细,使得例如,而不必void Rectangle::setWidthAndHeight(int x, int y)你应该有一个setWidth和独立setHeight

也许你有。一个构造函数,设置东西,和一个单一的功能doesIt,不管“它”,然后你有函子,和一个doIt可能是有意义的。例如,class Add implements Operation { Add(int howmuch); Operand doIt(Operand rhs);}

(但你可能会发现,你真的想要像访问者模式 - 一个纯粹的f如果你有纯粹的价值对象,那么unctor更有可能;如果它们被安排在一棵树中,并且彼此相关,访客就更有可能)。

即使有这么多小对象,单功能是正确的粒度级别,你可能想要像Facade Pattern这样的东西来构造出原始操作,经常使用的复杂操作。

没有人回答。如果你真的有一堆仿函数,那很酷。如果你真的只是把每个免费的功能变成一个班级,那是愚蠢的。

真正的答案在于回答这个问题:“我管理的是什么状态,以及我的班级如何模拟我的问题域?”

0

我会猜测,如果我没有看到代码给出了确定的答案。

但是,这听起来像你担心,这是审查代码的明确标志。你的问题的简短回答可以回到definition of Simple Design。最少数量的类和方法就是其中之一。如果您觉得您可以拿走一些元素而不会失去其他所需的属性,请继续并折叠/内联它们。

一些指针,以帮助您决定:

  • 你有“单一职责”好好检查一下?这是一个关键技能(我仍然没有看到它像主人),看起来很困难。它不一定会转化为一种方法 - 类。一个好的标准是每班有5-7个公共方法。每个班级可以有0-5个合作者。同时为了验证SRP,问问什么可以推动到这个类的变化?如果存在多个不相关的答案(例如,分组结构(解析)中的改变+将分组内容改变为动作映射(命令调度器)),那么可能需要对该类进行拆分。另一方面,如果你觉得数据包结构发生变化,会影响4个不同的类别 - 你已经跑掉了另一个悬崖;也许你需要把他们合并成一个有凝聚力的班级。
  • 如果您在命名具体实现时遇到困难,也许您不需要该接口。例如XXXImpl课程暗示XXX需要被关注。最近我了解到一个命名约定,其中接口描述了一个角色,并且实现是通过用于实现角色的技术来命名的(或者回退到它所做的)。例如XmppAuction实现拍卖(或狙击手通知器实现AuctionEventListener)
  • 最后你是否发现很难添加/修改/测试现有代码(例如测试设置很长或很痛苦)?这些可能是你需要重构的标志。
相关问题