何时使用mixin以及何时在Dart中使用接口?

nbr*_*bro 37 oop abstract-class interface mixins dart

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

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

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

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 {
}
Run Code Online (Sandbox Code Playgroud)

在Dart中,mixin也是通过普通的类声明来定义的......

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

mixin基本上是一个可以定义未实现或实现的方法的类.这是一种将方法添加到另一个类而无需逻辑上使用继承的方法.在Dart中,mixin应用于超类,通过"正常"继承扩展,如下例所示:

class A {
  void m() {
    print("method m");
  }
}

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

class B extends A with MyMixin {
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们应该注意到,B不必同时实现的任何进一步的方法AMyMixin.

将mixin应用于类并从类继承之间存在明显的区别,至少在仅支持单父继承的语言中,因为在这种情况下,我们可以将许多mixins应用于类,但类可能只是继承自另一个类.

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

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

何时使用mixin以及何时在Dart中使用接口?在设计软件时,是否有一些经验法则至少要有特殊的循环模式,最好定义一个mixin并将其应用于超类,而不是让我们的类实现一个接口?我希望在可以使用接口和mixin的上下文中设计决策的具体示例,但是一个用于另一个(由于某种原因).

lrn*_*lrn 20

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如果打算用作一个mixin(和记录这样).类作者可以对一个会破坏其作为mixin使用的类进行许多更改.我们不希望禁止任何此类更改,这对于非mixin类来说可能是完全合理的更改,因此使用非mixin类作为mixin是脆弱的,并且可能在将来中断.

另一方面,一个打算用作mixin的类通常都是关于实现的,因此很可能会声明一个类似的接口,这就是你应该在implements子句中使用的.

因此,如果您想要实现一个列表,您可以实现List该类并自己完成所有实现,或者在ListMixin类中混合以重用一些基本功能.你仍然可以写implements List<T>,但你可以通过继承得到ListMixin.

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

Dart中的Mixins通过创建一个新类来实现,该类将mixin的实现层叠在一个超类之上以创建一个新类 - 它不是"在旁边"而是在"超级"的"顶部",所以没有歧义如何解决查找问题.

例:

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());
  }
}
Run Code Online (Sandbox Code Playgroud)

真正发生的是你创建了一个新的类"Operation with Counter".它相当于:

例:

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());
  }
}
Run Code Online (Sandbox Code Playgroud)

的混入应用CounterOperation创建一个新的类,并且该类出现在超类链AutoStepOperation.

如果你这样做,class X extends Y with I1, I2, I3那么你创建了四个类.如果您这样做,class X extends Y implements I1, I2, I3那么您只创建一个类.即使所有的I1,I2并且I3都是完全空的抽象接口,使用with它们等同于:

class $X1 extends X implements I1 {}
class $X2 extends $X1 implements I2 {}
class $X3 extends $X2 implements I3 {}
class X extends $X3 {}
Run Code Online (Sandbox Code Playgroud)

你不会直接写,所以你应该使用with其中任何一个

  • 接口、继承和 mixin 的概念本身就很好,但 Dart 通过将这些概念都放在类中而使它们变得混乱。这就是为什么没有任何解释让人感到满意的原因。例如,为什么我们不应该使用 mixins 作为类型或接口?它们可以/应该被视为 Rust 中的特征吗?但另一方面,这并不是真正有用,因为泛型上的多个类型界限是不可能的。当每个容器类型都通过 mixins 实现时,为什么还要继承呢?感觉非常不连贯。 (4认同)
  • mixin 是否也有抽象方法?如果它们用于重用代码,那么拥有未实现的方法对它们来说没有多大意义。 (3认同)
  • `SetBase`扩展`SetMixin`而不是混合它只是因为`extends SetMixin`比使用SetMixin的`extends Object'更短(因为它可以).我们通常说你不能混在一个不适合它的类中(命名或记录为可用作mixin),因为这样做很可能在将来中断.这就是为什么我们有两个类,如`ListBase`和`ListMixin`,它们不相同(一个有一个const构造函数),我们有`SetBase`和`SetMixin`用于对称,即使`SetMixin`(当前)已经足够了. (3认同)
  • 我注意到在 Dart 中,特别是在库“collections”和“set.dart”部分中,它定义了一个“SetMixin”,它实际上声明了抽象(未实现)方法。从它的名字来看,`SetMixin`应该是一个mixin,但它被用作一个超类,因为`SetBase`实际上扩展了它。该文件中最奇怪的事情是“BaseSet”基本上只做了一件有用的事情:扩展“SetMixin”。为什么不简单地在`BaseSet`中声明`SetMixin`的方法呢? (2认同)

erl*_*man 8

不要害怕mixin,它来帮忙

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

但是 mixin 类似于:

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

当与配对StatefulWidgetStateTickerProviderStateMixin创建ticker与每一个帧,其是必要通过每AnimationController蜱。当有状态小部件处理时,它也会处理自动收报机。这就是我们在每个 AnimationController 中提供thisTickerProvider( vsync)的原因。

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

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

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

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

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

现在,如何使用 mixin :

任何类或抽象类都可以用作 mixin。但是如果我们声明了mixin,它就不能像普通类或抽象类那样进行扩展。

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 ?
Run Code Online (Sandbox Code Playgroud)

但是一个 mixin 不能使用另一个 mixin。

mixin C with B{} // Invalid ?
Run Code Online (Sandbox Code Playgroud)


Has*_*jmi 7

mixin与 swift 中的相同protocols。您可以快速定义具有默认实现的协议。mixin 也提供了这个功能。

如果你想在遵守这些协议的同时提供协议的默认实现,或者想要遵守多个协议,请使用 mixin。否则使用接口。


Rei*_*nds 6

诸如Java和C#之类的语言使用接口来具有类型的多重继承,而不是多重实现继承。对于复杂性的权衡,具有多个实现继承的语言(例如Eiffel,C ++或Dart)必须处理Java和C#的设计人员选择避免的问题。

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

例:

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();
}
Run Code Online (Sandbox Code Playgroud)

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

通过使用继承/混合和隐式接口implements通常是正交的;您很可能会结合使用它们,而不是彼此代替。例如,您可能想使用implements Set<int>描述一个位集实现的所需接口,然后使用extendsand / or with子句为该接口引入实际实现。原因是您的位集不会与共享任何实际的实现Set<int>,但您仍然希望能够互换使用它们。

集合库SetMixin为我们提供了一个mixin,它只需要我们自己实现一些基本例程,并Set<T>根据这些例程提供其余的实现。

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; }
}
Run Code Online (Sandbox Code Playgroud)


no_*_*ate 5

区别在于概念。如果你理解了这一点,你就会以正确的方式使用它。

  1. 在 OOP 中,接口是强制派生类实现一组公共字段和方法列表的东西。

但与 C# 和 JAVA 等其他传统编程语言不同,Dart 没有显式接口类型。默认情况下,每个类都定义了自己的由公共字段和方法组成的接口。因此,每个类都可以作为 Dart 中的一个接口。

implements关键字是实现一个接口。此外,一个类可以实现多个接口。

  1. 在 OOP 中,继承意味着在类之间共享行为。我们无法通过界面共享功能。所以,当我们实现一个类时,我们不能共享它的行为。

如果你想在这两个类之间共享行为,你应该使用extends关键字。

  1. 在 OOP 中,mixin 是一个包含供其他类使用的方法的类。与接口和继承方法不同,mixin 不必是其他类的父类。

所以 mixin 既不强加使用限制,也不强制类型限制。

您通常会将常用函数放在 mixin 中。通过使用with关键字来使用 mixin 。

取自这里