在使用依赖注入时,我曾经犯过将接口与具体类之间的关系设为一对一的错误。当我需要向接口添加一个方法时,就会破坏所有实现该接口的类。
这只是个简单例子,但假设我需要向其中一个类注入ILogger
。
public interface ILogger
{
void Info(string message);
}
public class Logger : ILogger
{
public void Info(string message) { }
}
像这样拥有一个一对一的关系感觉就像是代码异味。由于我只有一个实现,如果我创建一个类并将Info
方法标记为虚拟以在我的测试中覆盖它,而不是只为单个类创建一个接口,是否存在任何潜在问题?
public class Logger
{
public virtual void Info(string message)
{
// Log to file
}
}
如果我需要另一个实现,我可以重写 Info
方法:
public class SqlLogger : Logger
{
public override void Info(string message)
{
// Log to SQL
}
}
如果每个类都具有特定的属性或方法会导致泄漏的抽象,那么我可以提取出一个基类:
public class Logger
{
public virtual void Info(string message)
{
throw new NotImplementedException();
}
}
public class SqlLogger : Logger
{
public override void Info(string message) { }
}
public class FileLogger : Logger
{
public override void Info(string message) { }
}
我之所以没有将基类标记为抽象类,是因为如果我后续需要添加其他方法,就不会破坏现有的实现。例如,如果我的FileLogger
需要一个Debug
方法,我可以更新基类Logger
而不会破坏现有的SqlLogger
。public class Logger
{
public virtual void Info(string message)
{
throw new NotImplementedException();
}
public virtual void Debug(string message)
{
throw new NotImplementedException();
}
}
public class SqlLogger : Logger
{
public override void Info(string message) { }
}
public class FileLogger : Logger
{
public override void Info(string message) { }
public override void Debug(string message) { }
}
再次说明,这只是一个简单的例子,但在什么情况下我应该优先选择接口呢?
SqlLogger
只是一个具体的Logger
,带有SqlLogPersistenceStrategy
。在大多数情况下,组合比继承更好。对于你的问题,接口隔离原则怎么样?ILogInfo
、ILogError
等等。 - plalx