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

我非常熟悉接口和抽象类的概念,但对 Mixins的概念不是特别熟悉。

现在,在 Dart 中,每个类 A都定义了一个隐式接口,可以通过使用 implements关键字由另一个类 B实现。没有明确的方法来声明接口,例如,在 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 基本上是一个既可以定义未实现方法也可以定义已实现方法的类。这是一种向另一个类添加方法而不需要逻辑上使用继承的方法。在 Dart 中,Mixin 被应用到一个超类,该超类通过“普通”继承进行扩展,如下面的示例所示:

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


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


class B extends A with MyMixin {
}

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

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

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

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

什么时候使用 Mixin,什么时候在 Dart 中使用接口?在设计软件时,是否有一些经验法则,至少是特殊的循环模式,在这些模式中,最好定义一个 Mixin 并将其应用到超类,而不是让我们的类实现一个接口?我希望在一个既可以使用接口又可以使用混合的上下文环境中使用具体的设计决策示例,但是由于某种原因,一个可以用在另一个上面。

37993 次浏览

Languages such as Java and C# use interfaces to have multiple inheritance of type in lieu of multiple implementation inheritance. There are complexity tradeoffs that languages with multiple implementation inheritance (such as Eiffel, C++, or Dart) have to deal with that the designers of Java and C# chose to avoid.

However, once you have multiple implementation inheritance, there is no real need to separately support multiple interface inheritance, as an interface then becomes just a special case of an abstract class with no instance variables and only abstract methods and interface inheritance is the same as inheriting from such a class.

Example:

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 has multiple implementation inheritance (via mixins), so it doesn't also need multiple interface inheritance as a separate concept, nor a way to separately define interfaces as standalone entities. Implicit interfaces (via the implements clause) are used to be able to document or verify that one class implements at least the same interface as another. For example, Int8List implements List<int>, although the underlying implementation is completely different.

Use of inheritance/mixins and implicit interfaces obtained through implements is generally orthogonal; you will most likely use them in conjunction rather than in lieu of one another. For example, you may want to use implements Set<int> to describe the desired interface of a bitset implementation, and then use extends and/or with clauses to pull in the actual implementation for that interface. The reason is that your bitset will not share any of the actual implementation with Set<int>, but you still want to be able to use them interchangeably.

The collection library provides a SetMixin mixin for us that only requires us to implement some basic routines ourselves and supplies the rest of the Set<T> implementation based on those.

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

Mixins is all about how a class does what it does, it's inheriting and sharing concrete implementation. Interfaces is all about what a class is, it is the abstract signature and promises that the class must satisfy. It's a type.

Take a class that is implemented as class MyList<T> extends Something with ListMixin<T> .... You can use this class as MyList<int> l = new MyList<int>(); or List<int> l = new MyList<int>(), but you should never write ListMixin<int> l = new MyList<int>(). You can, but you shouldn't, because that is treating ListMixin as a type, and it really isn't intended as one. It's the same reason you should always write Map m = new HashMap(); and not HashMap m = new HashMap(); - the type is Map, it's an implementation detail that it's a HashMap.

If you mix in a class (or rather, the mixin derived from a class), then you get all the concrete members of that class in your new mixin class. If you implement a class (or rather, the implicit interface of a class), then you get no concrete members at all, but the abstract signature becomes part of your interface.

Some classes can be used as both, but you should only ever use a class as a mixin if it is intended to be used as a mixin (and documented as such). There are many changes that an class author can do to a class that would break their use as a mixin. We don't want to disallow any such change, which could be perfectly reasonable changes for a non-mixin class, so using a non-mixin class as a mixin is fragile and likely to break in the future.

On the other hand, a class intended to be used as a mixin is usually all about implementation, so it is likely that there is a similar interface declared as well, and that's what you should use in the implements clause.

So, if you want to implement a list, you can either implement the List class and do all the implementation yourself, or mix in the ListMixin class to reuse some base functionality. You can still write implements List<T>, but you get that by inheritance from ListMixin.

Mixins is not a way to get multiple inheritance in the classical sense. Mixins is a way to abstract and reuse a family of operations and state. It is similar to the reuse you get from extending a class, but it is compatible with single-inheritance because it is linear. If you have multiple inheritance, your class has two (or more) superclasses, and you need to handle conflicts between them, including diamond inheritance, in some way.

Mixins in Dart work by creating a new class that layers the implementation of the mixin on top of a superclass to create a new class - it is not "on the side" but "on top" of the superclass, so there is no ambiguity in how to resolve lookups.

Example:

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

What really happens is that you create a new class "Operation with Counter". It's equivalent to:

Example:

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

The mixin application of Counter to Operation create a new class, and that class appears in the superclass chain of AutoStepOperation.

If you do class X extends Y with I1, I2, I3 { ... } then you create four classes. If you just do class X extends Y implements I1, I2, I3 { ... } then you only create one class. Even if all of I1, I2 and I3 are completely empty abstract interfaces, using with to apply them is equivalent to:

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 */ }

You wouldn't write that directly, so you shouldn't write it using with either

dart interfaces, like another language, define a contract to any class to implement, this contract its required implement his public properties and methods

mixin it's just the another way to add functionality to your class because in dart not exist multi extends.

The difference lies in the concept. If you understand this, you will use it in the right way.

  1. In OOP, an interface is something that enforces the deriving class to implement a set list of public fields and methods.

But unlike other traditional programming languages like C# and JAVA, Dart does not have explicit interface types. Each class, by default, defines its own interface composed of public fields and methods. So, every class can act as an interface in Dart.

implements keyword is to implement an interface. Also, a class can implement multiple interfaces.

  1. In OOP, inheritance implies sharing of behavior between classes. We can not share features with an interface. So, when we implement a class, we can not share its behavior.

If you want to share the behavior across these two classes, you should use the extends keyword.

  1. In OOP, a mixin is a class that contains methods for use by other classes. Unlike the interface and inheritance approach, a mixin doesn’t have to be the parent class of those other classes.

So a mixin neither imposes usage restriction nor forces type restriction.

You will usually put common functions inside a mixin. Make use of the mixin by using the with keyword.

Taken from here

Don't be afraid with mixin, it's here to help 🎉

💣 Spoiler : mixin is not related to Animation by any means 😂, its just another keyword like class

But mixin is similar to :

fastfood 🍔/ plugin 🧩/ an interface with already implemented methods & state, that is ready to be used without reimplementing those features everywhere we need them

When paired up with a StatefulWidget's State,TickerProviderStateMixin creates ticker that ticks with every frame which is need by every AnimationController . It also disposes ticker when stateful widget disposes. That's why we provide this as TickerProvider(vsync) in every AnimationController.

Similarly we use ListMixin to use obvious implementation of List so that we do not have to implement obvious stuffs in every List implementation like ElementList,NodeList,FileList,TouchList etc.

Now let's try to compare and contract extends, implements and mixin

extends (inheritance) => only one class can be inherited along with their public/protected members and behaviours.

implements (contract) => many classes can be implemented but we have to redefine every behaviour.

with(mixin) => Many classes can be mixed in and we can reuse the behaviour of them.

Now, how to use mixin :

Any class or abstract class can be used as mixin. But if we declare mixin, it cannot be extended like normal class or abstract class.

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 ✅

But a mixin cannot use another mixin.

mixin C with B{} // Invalid ❌

mixin is same as protocols in swift. in swift you can define protocols with default implementation. mixin also provide this capability.

if you want to provide default implementation for protocols while conforming to these protocols or want to conform multiple protocols use mixin. otherwise use interface.

when to use mixin : this way we don't need to define pushup(), squat() in both abstract classes

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