【问题标题】:When to use variable of Interface type rather than concrete type何时使用接口类型而不是具体类型的变量
【发布时间】:2012-11-29 16:56:11
【问题描述】:

我有一个派生自接口的类。现在该类必须实现接口中的所有方法 + 它还定义了另外 2 个方法。 现在我的问题是,这样做有什么好处/用例:

IMyInterface varInt= new ConcreteImp();

超过,

ConcreteImp varInt= new ConcreteImp(); 

我看到这种模式在代码块中的每个地方都使用过,但不确定为什么要使用它。

标签: c#


【解决方案1】:

使用接口的好处是减少了部件对软件组件具体实现的依赖。在您发布的一行中,您将看不到好处。该接口的消费者可以从中受益。

编辑:你会很好read this article on abstractions

例如,假设您有一个方法接受像Rent(IMovie) 这样的接口。另一个人将能够编写Rent() 方法的实现,而无需知道调用该方法时将传入的IMovie 类型的细节。然后,您将能够创建多个不同的IMovie 实现,它们可能具有不同的计费方法,但Rent() 方法不必处理这些问题。

void Rent(IMovie movie)
{
    var price = movie.Price();
    movie.MarkReserved();
}

public interface IMovie { }

public class Oldie : IMovie
{
    private decimal _oldieRate = 0.8;

    public decimal Price()
    {
        return MainData.RentPrice * _oldieRate;
    }
    public decimal MarkReserved()
    {
        _oldiesDb.MarkReserved(this, true);
    }
}


public class Blockbuster : IMovie
{
    private decimal _blockbusterRate = 1.2;

    public decimal Price()
    {
        return MainData.RentPrice * _blockbusterRate ;
    }
    public decimal MarkReserved()
    {
        _regularDb.MarkReserved(this, true);
    }
}

这是为什么接口有用的示例,但不是代码设计的很好示例。

根据经验,您应该编写方法,以便它们需要最少的输入来工作,并且它们的输出提供尽可能多的信息供其他人调用时使用。例如,看一下以下签名:

public List<Entity> Filter(IEnumerable<Entity> baseCollection){ ... }

此方法仅请求 IEnumerable&lt;Entity&gt;,因此它可以采用不同的集合类型,例如 List&lt;Entity&gt;Entity[] 或某些工具返回的自定义类型。但是您返回List&lt;Entity&gt;,这样您就不会立即将调用者限制为可枚举的元素。例如,她可以立即在返回值上使用 Linq。

还有更多好处,例如在单元测试中,您可以创建模拟对象并告诉它们在与其余代码交互期间如何表现。虽然,您现在可以使用具有虚拟方法的类来做到这一点。

【讨论】:

  • 如果我们使用抽象类或带有虚拟方法的类,也可以实现上述所有优点。而且类比接口更灵活。所以我看不出有任何理由在类上使用接口,除非需要多重继承。
  • 除了你的评论部分是你提出的问题的核心。如您所知,.NET 中不存在多重继承,如果一个类已经继承了一个,您将无法向它添加另一个抽象。此外,IEnumerable&lt;T&gt; 的示例是您在现实世界中经常看到的一个非常具体的示例。您如何将 NHibernate 中的集合映射到抽象类而不是 IList&lt;T&gt;?除非必要,否则您不得强加于您的类的其他实现,而且通常您甚至不需要抽象类,因为您不需要共享代码。
【解决方案2】:

假设您希望在代码的其他地方能够将IMyInterface 的不同实现分配给varInt 变量。然后该变量需要用IMyInterface 类型声明。

或者,如果您想让任何代码阅读者清楚您打算对varInt 做的所有事情就是使用IMyInterface 定义的接口,那么类型声明就会清楚地说明这一点。

【讨论】:

    【解决方案3】:

    当您需要在派生类使用接口中强制执行功能时。

    当您需要将数据从超类传递到子类时,您可以使用具体类。它是接口和子类背后的基本 oop 思想。

    【讨论】:

      【解决方案4】:

      在您的具体示例中,我想说这并不重要,因为您使用的是new 并创建了一个具体类型。当您开始使用dependency injection 时,它开始变得更加有用。

      它更有用的场景如下所示:

      public SomeResultType DoSomething(ISomeType obj)
      {
          //to something with obj
          // return someResultType
      }
      

      只要实现ISomeType,就可以使用任何类型调用上面的代码。但在您使用new 关键字的示例中,我将改为使用var。您仍然可以将其视为它实现的类型,因为它继承了该类型。

      【讨论】:

        【解决方案5】:

        假设 IMyInterface 有“Draw”方法,现在所有派生类都必须实现“Draw”方法。如果您有一个带有方法“Render(IMyInterface shape)”的类“Engine”,那么无论形状是什么,您都只需要调用“Draw”方法。并且每个形状都随心所欲地绘制自己。 你可以看看设计模式,你会看到接口的魔力;)

        【讨论】:

          猜你喜欢
          • 2011-05-14
          • 1970-01-01
          • 2018-11-10
          • 1970-01-01
          • 2011-02-10
          • 2016-02-14
          • 1970-01-01
          • 1970-01-01
          • 2023-03-30
          相关资源
          最近更新 更多