【问题标题】:How to keep my functions (objects/methods) 'lean and mean' [closed]如何让我的功能(对象/方法)“精益求精”[关闭]
【发布时间】:2010-06-09 11:52:46
【问题描述】:

在我读到的所有(敏捷)文章中:保持代码和功能小而易于测试。

我应该如何使用“控制器”或“协调器”类来做到这一点?

在我的情况下,我必须导入数据。最后我有一个对象来协调这个,我想知道是否有一种方法可以保持协调器的精益(er)和平均(er)。

我的协调员现在执行以下操作(伪代码)

//Write to the log that the import has started
Log.StartImport()
//Get the data in Excel sheet format
result = new Downloader().GetExcelFile()
//Log this step
Log.LogStep(result )
//convert the data to intern objects
result = new Converter().Convertdata(result);
//Log this step
Log.LogStep(result )
//write the data
result = Repository.SaveData(result);
//Log this step
Log.LogStep(result )

恕我直言,这是“无所不知”的课程之一,还是至少有一个“不精益求精”的课程? 或者,我是否将这种精益和卑鄙的事情做得很远,如果没有某种“胖”进口商/协调员就不可能对进口进行编程吗?

米歇尔

编辑 这实际上是一个二合一的问题:一是如何测试它,二是是否可以有一个“了解所有/粘合在一起”的协调员

【问题讨论】:

  • 如果您更好地命名“结果”变量,您的伪代码可能会更清晰。我假设它不是每次都一样。

标签: c# .net unit-testing agile


【解决方案1】:

我相信很多人会不同意,但我认为您的方法总体上足够精简。但是,您在这里缺少的关键部分是依赖注入(也称为控制反转)-因此,您应该为这些类定义接口并将它们“注入”到您的构造函数中,而不是在您的方法中添加新的 Downloader、Converter 等。类:

private Downloader downloader;
private Converter converter;

public MyClass(Downloader downloader, Converter converter)
{
  this.downloader = downloader;
  this.converter = converter;
  //same with repository, it can't be a static class anymore
}

那么你只需在你的方法中使用这些实例:

//Write to the log that the import has started
Log.StartImport()
//Get the data in Excel sheet format
result = downloader.GetExcelFile()
//Log this step
Log.LogStep(result )
//convert the data to intern objects
result = converter.Convertdata(result);
//Log this step
Log.LogStep(result )
//write the data
result = repository.SaveData(result);
//Log this step
Log.LogStep(result )

现在进行此更改后,在您的测试中,您可以使用其中一个模拟框架(我使用 RhinoMocks)将依赖项的模拟实现注入到您的类中。这样您就可以验证转换器和下载器是否调用了正确的方法,而无需从磁盘读取任何内容并解析任何电子表格。

如何使用 RhinoMocks 的示例在他们的文档中:http://ayende.com/wiki/Rhino+Mocks+Documentation.ashx

如果您遇到问题,请不要犹豫,再问一个问题 :)

【讨论】:

  • 感谢您的回答。我读过很多关于模拟的文章,但从来不敢使用它。如果我使用模拟,我仍然需要对“真正的”Excel 阅读器进行单元测试,不是吗?在某些时候,我必须测试下载器是否真的从某个 Uri 获得 Excel 表格,不是吗?或者该测试不再称为单元测试而是集成测试?
  • 我也会为 Log 创建一个接口。日志是一个隐藏的依赖,不能像现在这样被嘲笑。
  • 对,我总是被困在日志依赖中
  • 嗨@Michel,(1)是的,你应该为每个类(2)在单元测试之上编写单元测试,你可能想要编写一些更高级别的测试(以测试集成类):请看一下这个讨论:stackoverflow.com/questions/2965483/…
  • @Michael 和 @Jakob 总会有隐含的协作者,例如日志记录或本机库,您不必模拟,而且真的不应该模拟,因为这是浪费时间。
【解决方案2】:

我遇到了类似的问题,我解决它的方法是真正正确地查看 SRP(单一责任主体)。

ExcelFileReader谁可以读取excel文件并返回一组List(行)

解析器它的工作是使用分隔符解析从 ExcelFileReader 返回的行

Importer,它处理将从 FileParser 返回的 DataSet 导入到正确的表中。

因为 ExcelFileReader 不依赖于 Parser 或 Importer,所以它保持在可测试的形式中。这适用于解析器,并且只需传递一个 TextReader 即可对其进行测试。

这有帮助吗?

【讨论】:

  • 问题是他的班级职责之一是与其他班级进行调解。理论上很容易将许多职责聚合到一个抽象中,但实际上它并不总是那么容易或对开发人员时间友好。
  • 这是我迄今为止所做的,我为每个主要步骤创建了类(读取 excel、转换和保存)。所以在不知道 SRP 的情况下,我已经使用了它:-)。但剩下的就是我有一个协调器,它从进口商那里获取 excel 表,将其交给转换器,取回转换后的数据,并将其交给存储库。中间也是把各种数据保存到logtables中,所以coordinator的方法('StartImport')是'fat'方法。
【解决方案3】:

拥有一个我认为是 mediator pattern 的类是很常见的。

我发现与必须有协作者的类(这是您现在遇到的情况)作斗争的一种方法是进行模拟单元测试

您实际上是在嘲笑您的合作者,并为这些合作者设定期望。

不幸的是,您使用的是 C#,而我不知道任何用于 C# 的 Mock 库。 但是我相信谷歌可以帮助你找到一个模拟库。

您可以只实现或扩展您的协作者的类,并使用产生预期输出的方法覆盖和实现您希望调用的方法,而不是模拟库。

正如 Michael 所指出的,如果您在协作者中进行依赖注入连接,它会变得容易得多。

【讨论】:

  • 中介者模式看起来很有前景
【解决方案4】:

您的 cmets 和记录器(单例?)太吵了:

Log.StartImport();
result = new Downloader().GetExcelFile()
Log.Step(result);
result = new Converter().Convertdata(result);
Log.Step(result);
result = Repository.SaveData(result);
Log.Step(result);

由于您必须一起完成三个步骤,因此将它们放在Converter 中。使用 DI 传入 ConverterDownloaderRepository。现在你最终得到:

Log.StartConvert();
convert.ExcelToRepository(); //passed in with DI

请注意,所有日志步骤结果都在相应的操作中完成。

【讨论】:

  • 不错的观点。我想知道我是否可以将日志记录放入相应的操作中,因为我认为 excel 转换器可能不应该对日志记录一无所知,因为这不是他关心的问题,而是转换器关心的更多问题?
  • 日志记录是一个横切关注点。如果您担心这些交叉问题,您可能会考虑使用一个消息系统,该系统具有多个订阅者到单个事件发布者。否则,将日志记录尽可能靠近操作。记住,告诉,不要问!。遵循得墨忒耳法则
【解决方案5】:

我发现 article 在确定我的代码是否确实精简和平均时特别有用

【讨论】:

    猜你喜欢
    • 2010-11-17
    • 1970-01-01
    • 1970-01-01
    • 2011-05-24
    • 1970-01-01
    • 2014-04-27
    • 2013-10-05
    • 2022-01-12
    • 1970-01-01
    相关资源
    最近更新 更多