我无法决定如何正确地重构代码。假设我有一个复杂的假设逻辑:
public class ImageService
{
public void UploadImage(VectorData data) {
var image = ConvertVectorDataToImage();
var thumbnail = CreateThumbnail(image);
SaveThumbnail(thumbnail);
SaveImage(image);
SendEmail(image);
...
}
}(如果例子中的某件事是出于单一的责任,那么让我们假装,我可以编一个更好的例子)
如果我使UploadImage可见(公共),那么编写测试就变得复杂了。它们往往很长,很难阅读,如果您想避免代码重复,那么测试中的逻辑就会出现。
如果我使所有这些小片段可见,API就会变得混乱(因为在我的示例中,每个人唯一感兴趣的是UploadImage方法)。更糟糕的是,经过一段时间之后,人们会倾向于使用那些小方法,因为它们是公开的,而不是因为它们应该在类之外使用。
因此,我的问题是,如何在可见性和测试复杂性之间取得平衡?还是我一开始就做错了什么?
发布于 2011-04-20 11:10:07
首先,您需要坚持,您应该支持组合而不是继承。这意味着,您不需要尝试通过子类来避免代码重复,而是需要从实现该行为的一组类中组合行为。
您不必担心所有这些类的可见性,就像客户机将看到的接口将是唯一可见的API.一样。
如果其他开发人员重用部分代码,这难道不是一件好事吗?这意味着您已经成功地创建了可重用的构建块。
发布于 2011-04-20 13:09:21
您应该开始使用模拟框架和代码来进行接口。
使用原子接口,尽可能使用SRP来描述要执行的任务:
IImageConvertor { ConvertVectorDataToImage }
IThumbnailWorker { CreateThumbnail; SaveThumbnail }埃塞特拉斯
现在,执行图像上传的类应该有以下每个接口的数据成员:
public class ImageService
{
IImageConvertor _imageConvertor;
IThumbnailWorker _thumbnailWorker;
}然后用你的方法调用
public void UploadImage(VectorData data) {
var image = _imageConvertor.ConvertVectorDataToImage();
var thumbnail = _thumbnailWorker.CreateThumbnail(image);
_thumbnailWorker.SaveThumbnail(thumbnail);
...
}现在您的模拟框架接管了,所以在测试中您为您的接口创建模拟对象,您的测试只是验证这些接口调用了正确的方法,并且它们被按正确的顺序调用。您实际上不做任何图像操作,也不进行任何文件访问。这样可以保持测试的快速性,并且确实有助于保持代码的解耦和模块化。
您可以分别测试作为每个接口基础的对象。通常,人们不测试诸如文件访问之类的东西,因为它们通常是由语言或第三方API处理的,您所做的只是包装它们,并且在单元测试中很快就会发现任何错误。你是否认为这是一个好主意,这是另一回事。
发布于 2011-04-20 10:29:39
src/main/java/net.bobah/example/MyClass.java:
public class MyClass {
void defaultMethod() { ... } // visible only to classes from the same package
}
src/main/java/net.bobah/example/MyClassTest.java:
public class MyClassTest {
@Test
void defaultMethodTest() { ... testInst.defaultMethod(); ... } // works fine
}https://softwareengineering.stackexchange.com/questions/69858
复制相似问题