最近我一直在尝试遵循TDD方法,这会产生很多子类,以便人们可以轻松地模拟依赖关系等。
例如,我可以说RecurringProfile
反过来又有可以应用的方法/操作,如MarkAsCancel
,RenewProfile
,MarkAsExpired
等。由于TDD,这些被实现为“小”类,如MarkAsCancelService
等。
为可以在RecurringProfile
上执行的各种方法/操作创建“外观”(单例)是否有意义,例如具有类RecurringProfileFacade
。然后,这将包含将代码委托给实际子类的方法,例如:
public class RecurringProfileFacade
{
public void MarkAsCancelled(IRecurringProfile profile)
{
MarkAsCancelledService service = new MarkAsCancelledService();
service.MarkAsCancelled(profile);
}
public void RenewProfile(IRecurringProfile profile)
{
RenewProfileService service = new RenewProfileService();
service.Renew(profile);
}
...
}
请注意,上面的代码不是实际代码,实际代码将使用构造函数注入的依赖项。这背后的想法是这样的代码的消费者不需要知道他们需要调用哪些类/子类的内部细节,而只需访问相应的“Facade”。
首先,这是'Facade'模式,还是其他形式的设计模式?
如果上述内容有意义,那么另一个问题是 - 如果考虑到它们没有任何特定的业务逻辑功能,你会对这些方法进行单元测试吗?
答案 0 :(得分:2)
如果您打算将代码作为库公开给别人,我只会创建这样的外观。您可以创建一个外观,这是其他人使用的界面。
这将为您提供更改实施的能力。
如果不是这样,那么这个立面提供了什么目的?如果一段代码想要在外观上调用一个方法,它将依赖于整个外观。最好保持依赖关系较小,因此在MarkAsCancelledService
上RecurringProfileFacade
依赖{{1}}时,调用代码会更好。
答案 1 :(得分:0)
在我看来,这是一种外观模式,因为你在简单的方法背后提取服务,虽然外观模式通常在我们的方法背后有更多的逻辑。原因是外观模式的目的是在更大的代码体上提供简化的界面。
关于你的第二个问题,我总是对所有事情进行单元测试。虽然,在您的情况下,它取决于,当您取消或更新个人资料时,它是否会更改项目的状态?因为你可以声称状态确实发生了你想象的变化。
答案 2 :(得分:0)
如果你的设计“告诉”你可以使用Singleton
为你做一些工作,那么它可能是糟糕的设计。 TDD应该引导你远离思考使用单身人士。
为什么这是一个坏主意(或可以是一个好主意)的原因可以找到on wikipedia
我对你的问题的回答是:看看其他模式!例如UnitOfWork
和Strategy
,Mediator
并尝试使用这些模式实现相同的功能,您将能够比较每个模式的好处。您最终可能会得到UnitOfStrategicMediationFacade
或其他内容; - )
考虑在Code Review发布此问题,以便进行更深入的分析。
答案 3 :(得分:0)
面对这类问题时,我通常会尝试从YAGNI / KISS的角度推理:
MarkAsCancelService
,MarkAsExpiredService
等吗?这些行动不会在RecurringProfile
本身有更好的家园吗? 您说这些服务是您的TDD流程的副产品,但TDD 1。并不意味着剥离所有逻辑的业务实体, 2。如果您做外化一些逻辑,它不必进入服务。如果您无法为提取的类提供比[BehaviorName]服务更好的名称,则通常表明您应该停止并重新考虑是否应该真正提取该行为。
简而言之,你的对象应该保持凝聚力,这意味着它们不应该包含太多的责任,但也不会变得贫血。
如果以上有意义,那么其他问题就是 - 你会对这些方法进行单元测试吗,考虑到它们没有 有任何特定的业务逻辑功能吗?
单元测试样板代码确实很痛苦。有些人会承受这种痛苦,有些人则认为这不值得。由于此类代码的重复性和可预测性,一个很好的折衷方案是自动生成测试,甚至更好地自动生成所有样板代码。