以下是《单元测试的艺术,第二版》一书的原文: 提取接口以允许替换底层实现
清单 3.1 提取一个涉及文件系统的类并调用它
public bool IsValidLogFileName(string fileName)
{
FileExtensionManager mgr =
new FileExtensionManager();
return mgr.IsValid(fileName);
}
class FileExtensionManager
{
public bool IsValid(string fileName)
{
//read some file here
}
}
清单 3.2 从已知类中提取接口
public class FileExtensionManager : IExtensionManager
{
public bool IsValid(string fileName)
{
...
}
}
public interface IExtensionManager
{
bool IsValid (string fileName);
}
//the unit of work under test:
public bool IsValidLogFileName(string fileName)
{
IExtensionManager mgr =
new FileExtensionManager();
return mgr.IsValid(fileName);
}
问题: 我不明白为什么添加接口可以更容易区分和替换后面的类调用?这个问题属于设计模式的范围吗?我应该学习一些设计模式来更好地理解这个问题吗?
不明白为什么添加接口可以更方便以后区分和替换类调用?
考虑到有限的例子,这是一种可以理解的混乱。
IsValidLogFileName
方法仍然与 FileExtensionManager
紧密耦合,因为它正在调用其构造函数。
在第一个和第二个示例中,如果您想更改为不同类型的扩展管理器,则需要更改一行代码。
这个问题属于设计模式的范围吗?我应该学习一些设计模式来更好地理解这个问题吗?
是的。查看依赖注入。如果
IsValidLogFileName
使用提供给它的 IExtensionManager
对象,而不是直接创建 new FileExtensionManager()
,那么您将完全从其代码中消除对 FileExtensionManager 的引用。
您可以通过简单地传入不同的对象来将
FileExtensionManager
替换为 BlobExtensionManager
或 FakeExtensionManager
或 EmptyExtensionManager
。您的方法的代码根本不需要更改!
您可以编写使用 mock
IExtensionManager
的单元测试来指定 IsValid
对于不同的值应返回什么,以验证 IsValidLogFileName
的行为是否符合其在各种情况下的行为方式。