依赖注入和显式接口实现
Dependency Injection and Explicit Interface Implementation
在依赖注入方面显式实现接口是否有好处?
据我了解,接口可以显式或隐式实现:
interface IFoo
{
void Bar();
}
//implicit implementation
class Foo1 : IFoo
{
public void Bar(){}
}
//explicit implementation
class Foo2 : IFoo
{
void IFoo.Bar(){}
}
现在显式实现只能通过调用接口方法来调用,而隐式实现可以直接在class的实例上调用:
class Baz
{
void Ba()
{
Foo1 foo1 = new Foo1();
foo1.Bar();
Foo2 foo2 = new Foo2();
foo2.Bar(); //syntax error
IFoo foo2_explicit = new Foo2();
foo2_explicit.Bar();
}
}
因此,使用显式接口实现,不会意外调用具体的方法 class,但必须调用接口方法。这是否像 DI 的一个目的那样防止紧密耦合的代码,或者我在这里咆哮错误的树?毕竟,一个人不能意外地编写一个构造函数或方法来注入具体 class 而不是接口:
class Baz
{
void Ba(Foo2 foo)
{
foo.Bar(); //syntax error
}
void Bb(IFoo foo)
{
foo.Bar();
}
}
如果您的 class 在容器中,那么您使用接口。所以,没有任何好处。
但是,如果您直接使用 class(例如在测试中),则必须强制转换才能访问该方法,这并不方便。
总计:在容器中使用 class 时有 0 个优点,但不利于测试。
通常,依赖注入的目的是解耦,你可以通过将抽象注入到它的客户端来实现:
public class Baz
{
private readonly IFoo foo;
public Baz(IFoo foo)
{
this.foo = foo;
}
// Members using this.foo go here...
}
这确保 Baz
依赖于 IFoo
,并且与任何具体实现 解耦 。
具体 class 实现 IFoo
是隐式还是显式都没有区别。
偶尔,一个 class 可能有一个 Concrete Dependency,但这不是特别正常;当它发生时,具体依赖是具体,所以通常根本不会实现接口。在这种情况下,显式与隐式接口实现无关紧要。
在我看来,通常应该始终保留对类型为 "just enough" 的对象的引用以供使用。考虑以下示例:
public interface IDo
{
void Do();
}
public interface IWatch
{
void Watch();
}
public class Foo : IDo, IWatch
{
public void Dummy() { }
public void Watch() { }
public void Do() { }
}
然后:
//I only want to use Do()
IDo aFoo = new Foo();
//I only want to use Watch()
IWatch bFoo = new Foo();
//I want to use stuff from Foo and optionally stuff from IDo or IWatch
Foo cFoo = new Foo();
在使用 MEF 或 Unity 等依赖注入容器时,您应该使用接口将对象导出到容器中,然后使用相同的接口类型将其导入。
遵循这些模式,我并没有真正看到使用显式接口实现的好处。 (这也使得在文本编辑器上方的标准 Visual Studio 组合框中定位您的实现方法更加困难)
在依赖注入方面显式实现接口是否有好处?
据我了解,接口可以显式或隐式实现:
interface IFoo
{
void Bar();
}
//implicit implementation
class Foo1 : IFoo
{
public void Bar(){}
}
//explicit implementation
class Foo2 : IFoo
{
void IFoo.Bar(){}
}
现在显式实现只能通过调用接口方法来调用,而隐式实现可以直接在class的实例上调用:
class Baz
{
void Ba()
{
Foo1 foo1 = new Foo1();
foo1.Bar();
Foo2 foo2 = new Foo2();
foo2.Bar(); //syntax error
IFoo foo2_explicit = new Foo2();
foo2_explicit.Bar();
}
}
因此,使用显式接口实现,不会意外调用具体的方法 class,但必须调用接口方法。这是否像 DI 的一个目的那样防止紧密耦合的代码,或者我在这里咆哮错误的树?毕竟,一个人不能意外地编写一个构造函数或方法来注入具体 class 而不是接口:
class Baz
{
void Ba(Foo2 foo)
{
foo.Bar(); //syntax error
}
void Bb(IFoo foo)
{
foo.Bar();
}
}
如果您的 class 在容器中,那么您使用接口。所以,没有任何好处。
但是,如果您直接使用 class(例如在测试中),则必须强制转换才能访问该方法,这并不方便。
总计:在容器中使用 class 时有 0 个优点,但不利于测试。
通常,依赖注入的目的是解耦,你可以通过将抽象注入到它的客户端来实现:
public class Baz
{
private readonly IFoo foo;
public Baz(IFoo foo)
{
this.foo = foo;
}
// Members using this.foo go here...
}
这确保 Baz
依赖于 IFoo
,并且与任何具体实现 解耦 。
具体 class 实现 IFoo
是隐式还是显式都没有区别。
偶尔,一个 class 可能有一个 Concrete Dependency,但这不是特别正常;当它发生时,具体依赖是具体,所以通常根本不会实现接口。在这种情况下,显式与隐式接口实现无关紧要。
在我看来,通常应该始终保留对类型为 "just enough" 的对象的引用以供使用。考虑以下示例:
public interface IDo
{
void Do();
}
public interface IWatch
{
void Watch();
}
public class Foo : IDo, IWatch
{
public void Dummy() { }
public void Watch() { }
public void Do() { }
}
然后:
//I only want to use Do()
IDo aFoo = new Foo();
//I only want to use Watch()
IWatch bFoo = new Foo();
//I want to use stuff from Foo and optionally stuff from IDo or IWatch
Foo cFoo = new Foo();
在使用 MEF 或 Unity 等依赖注入容器时,您应该使用接口将对象导出到容器中,然后使用相同的接口类型将其导入。
遵循这些模式,我并没有真正看到使用显式接口实现的好处。 (这也使得在文本编辑器上方的标准 Visual Studio 组合框中定位您的实现方法更加困难)