为什么类现在往往被定义为接口?

时间:2010-07-19 13:44:30

标签: c# design-patterns interface

过去2-3年,我看到的许多项目,如Cuyahoga开源C#CMS,倾向于将持久性和非持久性类定义为Interface。为什么?有充分的理由吗? TDD?惩戒?一个设计模式? ...

5 个答案:

答案 0 :(得分:21)

主要原因是这使得像dependency injection这样的技术更容易。这反过来又允许软件具有更大的灵活性,并且更容易重用和重新组合现有代码。这有用的示例包括各种形式的单元测试(如您所述),以及大多数其他形式的“常规”代码重用。

一个简单的例子:

假设你有一个计算emplyoee工资的方法。作为其签名的一部分,它接受一个计算其好处的对象,例如BenefitCalculator的一个实例:

calculateSalary(... BenefitCalculator bc, ...)

最初,您的设计只有一个类BenefitCalculator。但后来,事实证明你需要不止一个课程,例如因为软件的不同部分应该使用不同的算法(可能支持不同的国家,或者因为算法应该是用户可配置的......)。在这种情况下,不是膨胀现有的BenefitCalculator实现,而是创建新的类是有意义的,例如, BenefitCalculatorFrance,或BenefitCalculatorSimple等。

现在,如果您使用签名

calculateSalary(... BenefitCalculator bc, ...)

,你有点搞砸了,因为你无法提供不同的实现。但是如果你使用

  

calculateSalary(... IBenefitCalculator   bc,...)

你可以让所有类都实现接口。

这实际上只是“松散耦合”的一个特例:需求尽可能少来自代码的其他部分。在这种情况下,不要求某一类;而只是要求存在某些方法,这正是接口的作用。

答案 1 :(得分:3)

首先,您不能将类定义为接口。你的类实现了一个接口。

接口用作启用多态行为的一种方法。实现接口的每个类都可以自由地指定它自己在接口中定义的方法的实现。以下面的例子为例:

您正在编写银行软件。你的任务是编写一个事务处理器。现在,您知道您需要处理不同类型的交易(存款,提款,转账)。您可以编写如下代码:

public class TransactionProcessor
{
    public void ProcessDeposit( // Process Deposit );
    public void ProcessWithdraw( // Process Withdraw );
    public void ProcessTransfer( // Process Transfer );
}

然后每当有人添加新的交易类型时,您必须修改您的类。或者,您可以:

public interface ITransaction { void Process(); }

public class TransactionProcessor
{
    public void ProccessTransaction(ITransaction t) { t.Process(); }
}

现在您不需要修改代码来处理新类型的事务。你只需要人们创建自己的实现ITransaction的类,你的类就会“只处理它”。

这允许您根据需要交换接口的实现。它还支持依赖注入和模拟框架单元测试等功能。

总的来说,它实际上只是让代码更灵活的另一种方式。

答案 2 :(得分:1)

接口的优势在于它们使您独立于实现,这是一件好事。

答案 3 :(得分:1)

在过去几年中,IoC容器在开发人员中非常受欢迎。 例如,Microsoft Practices的Unity Container。因此,在应用程序开始时,您可以注册实现接口的具体类,然后,例如,当通过实例化对象实例化时,将填充在其构造函数中包含这些接口的所有类,或者标记有[Dependency]属性的属性。 Unity容器的决心。它在具有复杂依赖性的应用程序中非常有用,当一个界面可以在三个不同的类别中实现时。 如果不使用接口,所有这些都无法实现。

答案 4 :(得分:0)

在一个非常无聊的层面上,接口也有助于加快编译速度。

public class A {
   B b;
}

public class B {
   public int getCount() {
       return 10;
   }
}

在这种情况下,每次对 B 进行内部更改时,编译器都需要重新评估 A 以确定是否需要重新编译。

相反,我们使用接口:

class A {
   IB b;
}

interface IB {
   int getCount();
}

class B : IB {
   public int getCount() {
       return 10;
   }
}

在这种情况下, A 仅取决于 IB 。对 B 的更改不需要在编译时考虑 A

在规模上,这种对短路依赖性评估的影响可以显着加速大型代码库的编译。当有很多类依赖于单个类会发生很大变化时,它会特别强大。

显然,只有当类对实现类没有静态依赖时,此编译时权益才有效。执行以下操作将完全失败这一好处:

class A {
    IB b = new B();
}

这就是Dependency Injection的用武之地.DI容器会构建一个 B 并将其作为 A 提供给 A 所以 不需要具有静态依赖性。