首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >何时使用混合器,何时使用Dart中的接口?

问何时使用混合器,何时使用Dart中的接口?
EN

Stack Overflow用户
提问于 2017-08-27 02:48:38
回答 7查看 37.9K关注 0票数 87

我非常熟悉接口和抽象类的概念,但不太熟悉mixins的概念。

现在,在Dart中,每个类A都定义了一个隐式接口,这个接口可以由另一个类B使用implements关键字来实现。没有明确的方法来声明接口,例如,在Java中,接口只包含未实现的方法(并最终包含静态变量)。在Dart中,由于接口是由类定义的,接口A的方法实际上可能已经实现,但是实现B的类仍然需要覆盖这些实现。

我们可以从以下代码中看到这种情况:

代码语言:javascript
复制
class A {
  void m() {
    print("method m");
  }
}

// LINTER ERROR: Missing concrete implementation of A.m
// Try implementing missing method or make B abstract.
class B implements A {
}

在Dart中,混合器也是通过普通类声明定义的.

..。原则上,每个类都定义了一个可以从中提取的混合元素。但是,在此建议中,只能从没有声明构造函数的类中提取mixin。这一限制避免了由于需要将构造函数参数传递到继承链中而引起的复杂情况。

mixin基本上是一个可以定义未实现或实现的方法的类。这是一种在不需要逻辑使用继承的情况下向另一个类添加方法的方法。在Dart中,mixin应用于超类,该类通过“正常”继承进行扩展,如下面的示例所示:

代码语言:javascript
复制
class A {
  void m() {
    print("method m");
  }
}

class MyMixin {
  void f(){
    print("method f");
  }
}

class B extends A with MyMixin {
}

在这种情况下,我们应该注意到,B不需要实现A和MyMixin的任何进一步方法。

在向类应用混音和从类继承之间有明显的区别,至少在只支持单亲继承的语言中是这样,因为在这种情况下,我们可以对类应用许多混合,但是类可以只从另一个类继承。

在实现接口和从类继承之间也有明显的区别。实现接口的类需要强制实现接口定义的所有方法。

因此,总之,实现接口的概念更多的是与实现接口的类建立契约,而mixins的概念(顾名思义)更多的是关于重用代码(而不是继承层次结构)。

何时使用混合器,何时使用Dart中的接口?在设计软件时,是否有一些经验法则至少适用于特殊的反复模式,而不是让我们的类实现一个接口,而不是让我们的类实现一个接口?我希望在可以同时使用接口和混合器的情况下提供具体的设计决策示例,但其中一个可以用于另一个(出于某种原因)。

EN

回答 7

Stack Overflow用户

发布于 2017-08-27 09:48:04

Mixins是关于一个类是如何完成它所做的,它是如何继承和共享具体实现的。接口是关于类是什么的,它是类必须满足的抽象签名和承诺。这是一种类型。

接受一个实现为class MyList<T> extends Something with ListMixin<T> ...的类。您可以使用这个类作为MyList<int> l = new MyList<int>();或List<int> l = new MyList<int>(),但是您不应该编写ListMixin<int> l = new MyList<int>()。您可以这样做,但您不应该这样做,因为这是将ListMixin视为一种类型,而实际上它并不打算作为一种类型。这与您应该始终编写Map m = new HashMap();而不是HashMap m = new HashMap();的原因相同--类型是Map,它是一个实现细节,它是一个HashMap。

如果您混合在一个类中(或者更确切地说,混合来自一个类),那么您将在新的mixin类中获得该类的所有具体成员。如果您实现一个类(或者更确切地说,实现一个类的隐式接口),那么您将完全没有具体的成员,但是抽象签名将成为接口的一部分。

有些类可以两者兼用,但如果要将类用作混合体(并将其记录为混合体),则只应将其用作混合体。类作者可以对类进行许多更改,这些更改会破坏类作为混合器的使用。我们不想让任何这样的改变,这可能是完全合理的变化,对一个非混音类,所以使用一个非混音类作为混音是脆弱的,并有可能打破在未来。

另一方面,打算用作混合体的类通常都是关于实现的,所以很可能也有一个类似的接口声明了,这就是您应该在implements子句中使用的。

因此,如果您想实现一个列表,您可以实现List类并自己完成所有的实现,或者混合使用ListMixin类来重用一些基本功能。您仍然可以编写implements List<T>,但是可以通过从ListMixin继承获得。

Mixins不是一种获得古典意义上的多重继承的方法。Mixins是一种抽象和重用一系列操作和状态的方法。它类似于扩展类所获得的重用,但它与单继承兼容,因为它是线性的。如果您有多个继承,那么您的类有两个(或更多)超类,您需要以某种方式处理它们之间的冲突,包括钻石继承。

Dart中的Mixins通过创建一个新的类来工作,该类将在超类之上对mixin的实现进行分层,以创建一个新的类--它不是“在旁边”,而是在超类的“顶层”,因此在如何解析查找方面没有任何歧义。

示例:

代码语言:javascript
复制
class Counter {
  int _counter = 0;
  int next() => ++_counter;
}
class Operation {
  void operate(int step) { doSomething(); }
}
class AutoStepOperation extends Operation with Counter {
  void operate([int step]) {
    super.operate(step ?? super.next());
  }
}

真正发生的是创建一个新的类“带计数器的操作”。相当于:

示例:

代码语言:javascript
复制
class Counter {
  int _counter = 0;
  int next() => ++_counter;
}
class Operation {
  void operate(int step) { doSomething(); }
}
class $OperationWithCounter = Operation with Counter;
class AutoStepOperation extends $OperationWithCounter {
  void operate([int step]) {
    super.operate(step ?? super.next());
  }
}

Counter到Operation的混合应用程序创建了一个新的类,该类出现在AutoStepOperation的超类链中。

如果您执行class X extends Y with I1, I2, I3 { ... },那么您将创建四个类。如果只执行class X extends Y implements I1, I2, I3 { ... },那么只创建一个类。即使所有的I1、I2和I3都是完全空的抽象接口,使用with应用它们也相当于:

代码语言:javascript
复制
class $X1 extends Y implements I1 { /* no members in I1 */ }
class $X2 extends $X1 implements I2 { /* no members in I2 */ }
class $X3 extends $X2 implements I3 { /* no members in I3 */ }
class X extends $X3 { /* members of X */ }

你不会直接写,所以你也不应该用with写它

票数 61
EN

Stack Overflow用户

发布于 2020-06-20 15:08:02

别害怕和mixin在一起,它是来帮忙的

剧透:mixin与动画无关,它只是另一个关键字,如class

,但mixin类似于:

快餐店/插件/已经实现的方法和状态的接口,可以随时使用,而无需在我们需要它们的任何地方重新实现它们

当与StatefulWidget的State配对时,TickerProviderStateMixin创建了与每个AnimationController都需要的每一个帧同步的ticker。当有状态的小部件处理时,它也会处理代码。这就是为什么我们在每个AnimationController中提供this as TickerProvider(vsync)。

类似地,我们使用ListMixin来使用列表的明显实现,这样我们就不必在每个列表实现中实现明显的东西,比如ElementList,NodeList,FileList,TouchList等等。

现在让我们尝试比较和收缩扩展、实现和混合

extends (inheritance) =>只能继承一个类以及它们的公共/受保护成员和行为。

implements (contract) =>可以实现许多类,但是我们必须重新定义每个行为。

with(mixin) =>可以混合许多类,我们可以重用它们的行为。

现在,如何使用mixin:

任何类或抽象类都可以用作混合体。但是,如果我们声明mixin,则不能像普通类或抽象类那样扩展.。

代码语言:javascript
复制
class A{} //Declaring class
mixin B{} //Declaring mixin
class C extends A{} // Valid ✅
class C implements A{} // Valid ✅
class C with A{} // Valid ✅
class C extends B{} // Invalid ❌
class C implements B{} // Valid ✅

,但是混合器不能使用其他的混合.。

代码语言:javascript
复制
mixin C with B{} // Invalid ❌
票数 17
EN

Stack Overflow用户

发布于 2017-08-27 04:17:33

像Java和C#这样的语言使用接口来具有多个类型的继承,而不是多个实现继承。具有多个实现继承的语言(如埃菲尔、C++或Dart)必须处理的复杂性权衡是Java和C#设计人员选择避免的。

但是,一旦您有了多个实现继承,就不需要单独支持多个接口继承,因为接口就变成了抽象类的特例,没有实例变量,只有抽象方法和接口继承与从此类继承相同。

示例:

代码语言:javascript
复制
abstract class IntA {
  void alpha();
}

abstract class IntB {
  void beta();
}

class C extends IntA with IntB {
  void alpha() => print("alpha");
  void beta() => print("beta");
}

void main() {
  var c = new C();
  IntA a = c;
  IntB b = c;
  a.alpha();
  b.beta();
}

Dart具有多个实现继承(通过混合继承),因此它也不需要将多个接口继承作为一个单独的概念,也不需要将接口单独定义为独立实体的方法。隐式接口(通过implements子句)用于记录或验证一个类实现至少与另一个类相同的接口。例如,Int8List实现List<int>,尽管底层实现完全不同。

通过implements获得的继承/混合和隐式接口的使用通常是正交的;您很可能会将它们合并使用,而不是相互替代。例如,您可能希望使用implements Set<int>来描述位集实现的所需接口,然后使用extends和/或with子句来提取该接口的实际实现。原因是您的位集不会与Set<int>共享任何实际实现,但是您仍然希望能够互换地使用它们。

集合库为我们提供了一个SetMixin混联器,它只要求我们自己实现一些基本例程,并在此基础上提供Set<T>实现的其余部分。

代码语言:javascript
复制
import "dart:collection";

class BitSetImpl {
  void add(int e) { ...; }
  void remove(int e) { ...; }
  bool contains(int e) { ...; }
  int lookup(int e) { ...; }
  Iterator<int> get iterator { ...; }
  int get length { ...; }
}

class BitSet extends BitSetImpl with SetMixin<int> implements Set<int> {
  BitSet() { ...; }
  Set<int> toSet() { return this; }
}
票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45901297

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档