代码之家  ›  专栏  ›  技术社区  ›  Afshar Mohebi

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

  •  13
  • Afshar Mohebi  · 技术社区  · 14 年前

    Interface . 为什么?有什么好的理由吗?TDD?嘲笑?设计模式。。。

    5 回复  |  直到 14 年前
        1
  •  21
  •   Community Romance    4 年前

    dependency injection 更容易的。这反过来又允许软件更灵活,更容易重用和重组现有代码。这很有用的例子包括各种形式的单元测试(如您所提到的),但也包括大多数其他形式的“常规”代码重用。

    一个简单的例子:

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

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

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

    现在如果你用签名

    计算报警(。。。收益计算器(bc,…)
    

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

    计算报警(。。。iBenefit计算器 bc,…)

    您可以让所有类实现接口。

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

        2
  •  3
  •   Justin Niessner    14 年前

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

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

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

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

    不过,总的来说,这只是使代码更灵活的另一种方法。

        3
  •  1
  •   codymanix    14 年前

    接口的优点是使您独立于实现,这是一件好事。

        4
  •  1
  •   Kru    14 年前

    在过去的几年中,IoC容器在开发人员中变得相当流行。 例如,Microsoft Practices的Unity容器。因此,在应用程序开始时,您可以注册实现接口的具体类,然后,例如,当通过Unity容器的解析实例化对象时,将填充在其构造函数中包含这些接口的所有类,或其标记为[Dependency]属性的属性。当一个接口可以在三个不同的类中实现时,它在具有复杂依赖关系的应用程序中非常有用。 所有这些都离不开接口的使用。

        5
  •  0
  •   Alain O'Dea    14 年前

    在一个非常枯燥的层次上,接口还可以帮助实现更快的编译。

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

    在这种情况下每次内部变化 如果生成,编译器需要重新计算 A 以确定是否需要重新编译。

    相反,我们使用接口:

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

    A 只取决于 伊布 B 需要考虑 A 在编译时。

    在规模上,这种对短路依赖性求值的影响可以显著加快大型代码库的编译。当有很多类依赖于一个变化很大的类时,它就特别强大了。

    显然,只有当类对实现类没有静态依赖时,这种编译时优势才有效。执行以下操作将彻底挫败这一优势:

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

    这就是依赖注入的用武之地。DI容器将构造一个 并提供给 A 作为一个 伊布 A 不需要有静态依赖关系。