什么时候在Dart中使用接口?

时间:2012-05-08 16:33:43

标签: inheritance interface dart

我正在阅读Dart的文档,我有点困惑,也许是因为我来自Ruby,关于如何使用接口。当然,接口不是Dart独有的,当有人应该使用接口时,有很多解释。例如,This one似乎在说接口仅在您加入团队时才有用。它甚至应该在开源世界中意味着什么,每个人都在阅读和重用其他人的代码?

我看到的一个有趣的解释似乎暗示使用了接口:

  1. 在缺少多重继承的语言中,
  2. 就此而言,它们可以作为没有多重继承的解决方法。
  3. 我不明白。我知道Ruby中的模块是一种解决方法,因为它们允许我用实际的主体定义真实的方法。接口只允许我定义实现它的类应该具有的方法。有什么收获?任何人都可以告诉我一个真正有用的例子,我可以立即看到使用接口的价值吗?

    P.S。在相关的说明中,有没有办法在Dart中使用多重继承?

3 个答案:

答案 0 :(得分:23)

更新:the interface keyword has since been removed from Dart


接口非常有用,因为它们允许您切换类的实现,同时仍允许验证传入的类型是否满足接口的要求。

采用以下(常用)示例:

interface Quackable {
  void quack();
}

这定义了将传递给方法的类的要求,例如:

sayQuack(Quackable quackable) {
   quackable.quack();
}

允许您使用Quackable对象的任何实现,例如:

class MockDuck implements Quackable {
  void quack() => print("quack");
}

class EnterpriseDuck implements Quackable {
  void quack() {
    // connect to three enterprise "ponds"
    // and eat some server bread
    // and say "quack" using an messaging system
  }

}

这两种实现都可以与sayQuack()函数一起使用,但是其中一个需要的基础设施要少得多。

sayQuack(new EnterpriseDuck());
sayQuack(new MockDuck());

在构建使用某些“企业鸭”的解决方案时,我一直在Java世界中使用这种模式。在本地开发时,我只需要能够调用sayQuack()函数并返回一些硬编码的模拟数据。

鸭子打字

因为Dart是可选的,所以你实际上并不需要使用该接口,只需编写一个包含正确方法签名的类就行了(尽管这些工具无法验证它)。

class Person {   // note: no implements keyword
  void quack() => "I'm not a duck";
}

sayQuack(new Person()); // provides the quack method, so this will still work

所有类都是接口

最后,所有类都是接口。这意味着即使第三方系统可能在不使用接口的情况下编写,您仍然可以使用具体类,就像它是一个接口一样。

例如,想象一下以下的企业库:

class EnterpriseDuck { // note: no implements keyword
  void quack() {
    // snip
  }
}

sayQuack(EnterpriseDuck duck) {  // takes an instance of the EnterpriseDuck class
  duck.quack();
}

并且您希望以类型检查器可以验证的方式将模拟鸭传递到sayQuack方法。您可以创建mockDuck来实现EnterpriseDuck隐含的界面,只需使用EnterpriseDuck作为接口:

class MockDuck implements EnterpriseDuck {
  void quack() => "I'm a mock enterprise duck";
}

多重继承

就多重继承而言,这在Dart中是不可能的。但是,您可以实现多个接口并提供所需方法的实现,例如:

class MultiDuck implements Quackable, EnterpriseDuck, Swimable {
  // snip...
}

接口可以有默认类

当您使用Dart时,您会发现大多数“类”实际上是接口。 List,String等...都是提供默认实现的接口。当你打电话

List myList = new List();

您实际上正在使用List接口,而new关键字从接口重定向到基础默认List实现。

关于在团队中发展

接口在团队开发中非常有用,即使在开源世界中也是如此。该接口定义了您应该构建的方法和属性,以便您的组件可以与我的组件一起使用。您可以构建自己的该接口的测试实现,并且我可以构建该接口的具体实现,当我们完成后,我们可以集成。如果没有已发布的共享界面,我需要在您真正开始之前提供我的具体实现。

希望有所帮助!

答案 1 :(得分:4)

基本上,接口与多重继承无关。在执行此操作时可以伪造多个继承和滥用接口,但如果您想要真正的多重继承(或mixins或traits),那么Dart不提供它们(目前 - 我相信mixins会在某一天找到它们的方式)。

什么是适合的接口是显式合同。假设您有两个需要彼此协作的组件AB。您肯定可以直接从B致电A,但它会有效,但下次您需要更改B时,您必须查看A它是如何使用它。那是因为 B没有暴露显式接口。是的,接口的正确用语不是工具,而是公开

如果您在界面后面隐藏B的实现,并且仅向A提供该界面,那么您可以随意更改B,并且只担心仍会暴露相同的界面。界面甚至可以被多个类暴露,调用者不必关心(甚至不知道)。

请注意, interface 这个词在这里有两个含义:组件的一般契约,也可以在文档中用英文描述,以及特殊语言构造,可帮助您在编程语言中描述(并强制执行)合同的某些部分。

您不一定非必须使用语言结构,但使用它来描述编程语言允许的合同部分被认为是好的风格。

现在,这里的合同到底是什么?简而言之,contract是组件对用户期望的内容以及用户对组件的期望的描述。

例如,假设我有一个计算数字绝对值的方法:

class MathUtils {
  /// Computes absolute value of given [number], which must be a [num].
  /// Return value is also a [num], which is never negative.
  absoluteValue(number) {
    ... here's the implementation ...
  }
}

此处的合同在文档注释中完整描述(嗯,不完全,我们也可以描述绝对值是什么,但这已经足够了)。嗯......但评论的某些部分可以用语言直接表达,对吗?

class MathUtils {
  /// Computes absolute value of given [number].
  /// Return value is never negative.
  num absoluteValue(num number) {
    ... here's the implementation ...
  }
}

请注意,合同的某些部分只是不能用编程语言表达 - 在这里,语言不知道绝对值是什么,这需要留在评论中。此外,你不能表示返回值永远不会是负数,所以这也必须保留在评论中。但事实上,你的代码的读者知道绝对值是什么(并且它永远不会消极),方法名称对目的非常明确,因此评论可以完全省略:< / p>

class MathUtils {
  num absoluteValue(num number) {
    ... here's the implementation ...
  }
}

所以现在,合同的某些部分是使用语言手段明确表达的,而某些部分是隐式表达的(你依赖于人们知道绝对值是什么)。

该语言中的接口用于将合同与实现分离。在小型程序中使用它可能有点过头了,但是在做大型程序时(这不需要涉及团队工作)会有所回报。

Uff,结果比我想象的要长。希望有所帮助。

答案 2 :(得分:1)

接口是Dart中类型系统的一部分,类型声明是可选的。这意味着接口也是可选的。

接口可帮助您记录对象响应的方法。如果您实现了接口,那么您承诺在接口中实现所有方法。

Dart使用此信息来显示类型不匹配的编译时警告,为代码辅助提供更有用的建议并协助进行一些重构。