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

Oop 何时在Dart中使用mixin以及何时使用接口?,oop,interface,dart,abstract-class,mixins,Oop,Interface,Dart,Abstract Class,Mixins,我非常熟悉接口和抽象类的概念,但不太熟悉mixin的概念 现在,在Dart中,每个类A都定义了一个隐式接口,可以由另一个类B使用implements关键字来实现。例如,在Java中,没有明确的方式来声明接口,接口只包含未实现的方法(以及最终的静态变量)。在Dart中,由于接口是由类定义的,因此接口A的方法实际上可能已经实现,但是实现B的类仍然需要覆盖这些实现 我们可以从以下代码中看到这种情况: class A { void m() { print("method m"); } }

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

现在,在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 {
}
在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 {
}
在这种情况下,我们应该注意,
B
不必实现
A
MyMixin
的任何进一步方法

将mixin应用于类和从类继承之间有着明显的区别,至少在一种只支持单亲继承的语言中是如此,因为在这种情况下,我们可以将许多mixin应用于一个类,但一个类只能从另一个类继承

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

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


何时在Dart中使用mixin以及何时使用接口?在设计软件时,如果定义一个mixin并将其应用于一个超类,而不是让我们的类实现一个接口,那么至少有一些特殊的重复模式的经验法则吗?我希望在一个既可以使用接口又可以使用混合的环境中给出具体的设计决策示例,但由于某种原因,一种是使用在另一种之上的。

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

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

集合库为我们提供了一个
SetMixin
mixin,它只需要我们自己实现一些基本例程,并提供基于这些例程的
Set
实现的其余部分

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; }
}
导入“省道:集合”;
类BitSetImpl{
void add(int e){…;}
void remove(int e){…;}
布尔包含(int e){…;}
int查找(int e){…;}
迭代器获取迭代器{…;}
int获取长度{…;}
}
类BitSet使用SetMixin实现集扩展BitSetImpl{
位集(){…;}
Set toSet(){返回此;}
}
Mixins是关于类如何做它所做的事情,它继承和共享具体的实现。 接口与类有关,它是类必须满足的抽象签名和承诺。这是一种类型

以实现为
类的类为例,MyList使用ListMixin扩展了一些东西…
。可以将该类用作
MyList l=new MyList()
列表l=new MyList()
,但决不能在l=new MyList()中写入
listmix。您可以,但不应该,因为这将
ListMixin
视为一种类型,而实际上它并不是一种类型。 这与您应该始终编写
Map m=newhashmap()的原因相同和非
哈希映射m=n
                mixin CommonExercise {
                  pushUp() => print("push up");
                
                  squat() => print("squat ");
                }
                
                abstract class Sports with CommonExercise {
                  StrengthImprovement();
                 // pushup(){}  // don't need to define
                 // squat(){}
                }
                
                abstract class Bodybuilding with CommonExercise {
                  muscleSizeIncrease();
                }