依赖注入和显式接口实现

9

在依赖注入方面,显式实现接口有什么好处吗?

据我所知,接口可以显式或隐式实现:

interface IFoo
{
    void Bar();
}

//implicit implementation
class Foo1 : IFoo
{
    public void Bar(){}
}

//explicit implementation
class Foo2 : IFoo
{
    void IFoo.Bar(){}
}

现在,显式实现只能通过调用接口方法来调用,而隐式实现可以直接在类的实例上调用:

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();
    }
}

因此,使用显式接口实现,不能意外地在具体类上调用方法,而必须调用接口方法。这是否可以防止紧密耦合的代码,就像 DI 的一个目的一样,或者我在这里搞错了吗?毕竟,不能意外编写一个构造函数或方法,将一个具体类注入而不是一个接口:
class Baz
{
    void Ba(Foo2 foo)
    {
        foo.Bar(); //syntax error
    }

    void Bb(IFoo foo)
    {
        foo.Bar();
    }
}

2
你能否更新你的代码,使用IFoo代替Foo吗?在我看来这样会更清晰易懂... - user3869175
完成 - 抱歉,通常我尊重代码规范... - Thaoden
3个回答

8

依赖注入的目的通常是解耦,通过将抽象注入到其客户端中来实现:

public class Baz
{
    private readonly IFoo foo;

    public Baz(IFoo foo)
    {
        this.foo = foo;
    }

    // Members using this.foo go here...
}

这确保了Baz依赖于IFoo,并且与任何具体实现解耦
无论具体类是否隐式或显式实现IFoo都没有影响。
偶尔会出现一个类有一个具体依赖项,但这并不是特别正常的情况;当发生这种情况时,具体依赖项是具体的,因此通常根本不实现接口。在这种情况下,显式与隐式接口实现都是无关紧要的。

1
这就是我试图表达的意思。如果由于某种模糊原因而编写了一个期望具体类的构造函数:public Baz(Foo1 foo),则使用显式接口实现将禁止调用对象的方法。试图将具体实现强制转换为其接口会触发警报,不是吗? - Thaoden
如果你不小心添加了一个期望具体类的构造函数,那么你就失去了依赖注入的目的——解耦。这是服务中更大的问题,应该从问题的根源解决,而不是通过某个任意客户端进行修补。 - Mark Seemann

1
如果您的类在容器中,则使用接口。因此,没有任何好处。
但是,如果您直接使用类(例如在测试中),则必须进行强制转换才能访问该方法,这不方便。
总计:在容器中使用类时没有任何优势,并且对于测试来说很糟糕。

0
在我看来,通常情况下,我们应该始终保留对类型“刚好足够”使用的对象的引用。考虑以下示例:
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文本编辑器上方的组合框中定位实现方法更加困难)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接