如果您使用接口或至少使用抽象/可继承类,您可以通过在 DI/IoC 配置中轻松交换实现(注入另一个类)来更改程序的行为。
使用接口是一种很好的做法(恕我直言)。如果您正在编写需要模拟的单元测试,这一点尤其重要。如果您不使用接口,那么编写具有良好覆盖率的 UnitTest(并不是说在大多数“现实世界”情况下是不可能的)要困难得多。
如果注入的部分有可能发生变化,我认为您应该使用接口。扩展您的实现应该很容易,请参阅Open-Closed-Principle。=> 这将需要交换模块/部件/实现...问问自己如果您的类没有要覆盖的虚函数并且您被迫更改实现会发生什么。
我至少会为你的代码的公共类/部分(其他程序员会使用的部分)使用接口。
看看你的样品。问题出在接线部分,而不仅仅是将类绑定为接口的(默认)实现(绑定有效,但接线可能会中断)。
例如,如果您有 2 个实现(此处的 C# 示例,在 Java 等中也应该相同):
public interface IUserStorage
{
void Write(object something);
}
public class UserStorageTextFile : IUserStorage
{
public void Write(object something) { ... }; // stores to text file
}
public class UserStorageDB : IUserStorage
{
public void Write(object something) { ... }; // stores to DB
}
public class MyStorageClient
{
public MyStorageClient(IUserStorage storage) { ... } // copy to private field and use it etc.
}
根据您的 IoC,将 MyStorageClient 的实例连接到您的 IUserStorage 绑定应该很容易。
bind(IUserStorage.class).to(UserStorageDB.class); // Java sample, eh?
但是,如果您的 MyStorageClient 已经被迫使用数据库...
public class MyStorageClient
{
public MyStorageClient(UserStorageDB storage) { ... } // copy to private field and use it etc.
}
...除了 UserStorageTextFile 是从 UserStorageDB 继承的之外,不可能将它与 UserStorageTextFile 类连接起来......但是如果您只想编写一个简单的文本文件,为什么要依赖于例如 Oracle 驱动程序(UserStorageDB 需要) ?
我认为该示例足够清晰,并显示了使用接口的好处...
但如果不是...尝试这样做:
bind(UserStorageDB.class).to(UserStorageTextFile.class);
// and in another config/module/unitTest
bind(UserStorageTextFile.class).to(Mock(UserStorageDB.class));
// and try to wire it against your client class, too (both ways, meaning one config for TextFile and load a config for the DB after changing only the configuration)