问题描述
我对接口和抽象类的概念非常熟悉,但对mixins的概念不是很熟悉.
I'm very familiar with the concepts of interfaces and abstract classes, but not super familiar with the concepts of mixins.
现在,在 Dart 中,每个类 A
都定义了一个隐式接口,该接口可以由另一个类 B
使用 implements
实现关键词.没有明确的方式来声明接口,例如,在 Java 中,接口只包含未实现的方法(以及最终的静态变量).在 Dart 中,由于接口是由类定义的,接口 A
的方法实际上可能已经实现了,但是实现了 B
的类仍然需要覆盖这些实现.
Right now, in Dart, every class A
defines an implicit interface, which can be implemented by another class B
by using the implements
keyword. There's no explicit way of declaring interfaces as, for example, in Java, where an interface contains only unimplemented methods (and eventually static variables). In Dart, since interfaces are defined by classes, the methods of the interface A
may actually already be implemented, but the class that implements B
still needs to override these implementations.
我们可以从以下代码中看到这种情况:
We can see that situation from the following piece of code:
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 也是通过普通的类声明定义的...
In Dart, a mixin is also defined via ordinary class declarations...
... 原则上,每个类都定义了一个可以从中提取的 mixin.然而,在这个提议中,mixin 只能从没有声明构造函数的类中提取.此限制避免了由于需要将构造函数参数向上传递到继承链而出现的复杂情况.
mixin 基本上是一个可以定义未实现或已实现的方法的类.这是一种向另一个类添加方法而无需在逻辑上使用继承的方法.在 Dart 中,mixin 应用于超类,通过普通"继承进行扩展,如下例所示:
A mixin is basically a class that can define both unimplemented or implemented methods. It's a way to add methods to another class without needing to logically use inheritance. In Dart, a mixin is applied to a super class, that is extended via "normal" inheritance, like in the following example:
class A {
void m() {
print("method m");
}
}
class MyMixin {
void f(){
print("method f");
}
}
class B extends A with MyMixin {
}
在这种情况下,我们应该注意 B
不必实现 A
和 MyMixin
的任何进一步方法.
In this case, we should note that B
does not have to implement any further methods both of A
and MyMixin
.
对类应用 mixin 和从类继承之间有明显的区别,至少在只支持单亲继承的语言中是这样,因为在这种情况下,我们可以应用许多mixins 到一个类,但一个类可以从另一个类继承.
There's a clear distinction between applying a mixin to a class and inheriting from a class, at least in a language that supports only single-parent inheritance, since, in that case, we could apply many mixins to a class, but a class could just inherit from another class.
在实现接口和从类继承之间也有明显的区别.实现接口的类需要强制实现接口定义的所有方法.
There's also a clear distinction between implementing an interface and inheriting from a class. The class that implements an interface needs to mandatorily implement all methods defined by the interface.
所以,综上所述,实现接口的概念更多是与实现接口的类建立契约,mixin的概念(顾名思义)更多的是重用代码(不重复继承)层次结构).
So, in summary, the concept of implementing an interface is more about establishing a contract with the class that implements the interface, and the concept of mixins (as the name suggests) is more about reusing code (without recurring to an inheritance hierarchy).
Dart 中什么时候使用 mixins,什么时候使用接口?在设计软件时,是否有一些经验法则至少适用于特殊的循环模式,在这种情况下,最好定义一个 mixin 并将其应用于超类,而不是让我们的类实现一个接口?在可以同时使用接口和混合的上下文中,我会欣赏设计决策的具体示例,但一个被使用在另一个之上(出于某种原因).
When to use mixins and when to use interfaces in Dart? Are there some rules of thumb for at least special recurrent patterns when designing software where it would be better to define a mixin and applying it to a super class rather than making our class implement an interface? I would appreciate concrete examples of design decisions in a context where both interfaces and mixins could be used, but one is used over the other (for some reason).
推荐答案
Mixins 是关于一个类如何做它所做的事情,它继承和共享具体的实现.接口是关于类是什么,它是类必须满足的抽象签名和承诺.这是一种类型.
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.
取一个实现为 class MyList 的类使用 ListMixin<T> 扩展某些东西...
.您可以将此类用作 MyList;l = new MyList();
或 List;l = new MyList()
,但你永远不应该写 ListMixin;l = new MyList()
.您可以,但您不应该,因为这将 ListMixin
视为一种类型,而实际上并不打算将其视为一种类型.这与您应该始终编写 Map m = new HashMap();
而不是 HashMap m = new HashMap();
- type 的原因相同是Map
,它是一个HashMap
的实现细节.
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
.
如果您混入一个类(或者更确切地说,是从类派生的 mixin),那么您将在新的 mixin 类中获得该类的所有具体成员.如果你实现一个类(或者更确切地说,一个类的隐式接口),那么你根本得不到具体的成员,但抽象签名成为你接口的一部分.
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.
有些类可以同时使用,但只有在打算用作 mixin(并记录为此类)时,您才应该将类用作 mixin.类作者可以对类进行许多更改,这会破坏它们作为 mixin 的使用.我们不想禁止任何此类更改,这对于非 mixin 类来说可能是完全合理的更改,因此将非 mixin 类用作 mixin 是脆弱的,并且将来可能会中断.
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.
另一方面,打算用作 mixin 的类通常都是关于实现的,因此很可能也声明了一个类似的接口,这就是您应该在 implements 子句中使用的接口.
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.
所以,如果你想实现一个列表,你可以实现List
类并自己完成所有的实现,或者混入ListMixin
类来重用一些基础功能.您仍然可以编写 implements List
,但是您可以通过从 ListMixin
继承来获得.
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 不是经典意义上的多重继承.Mixins 是一种抽象和重用一系列操作和状态的方法.它类似于您从扩展类中获得的重用,但它与单继承兼容,因为它是线性的.如果您有多重继承,您的类有两个(或更多)超类,您需要以某种方式处理它们之间的冲突,包括菱形继承.
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.
Dart 中的 Mixin 通过创建一个新类来工作,该类将 mixin 的实现分层在超类之上以创建一个新类——它不是在旁边"而是在超类之上",所以有在如何解决查找方面没有歧义.
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.
示例:
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());
}
}
真正发生的是您创建了一个新类Operation with Counter".相当于:
What really happens is that you create a new class "Operation with Counter". It's equivalent to:
示例:
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
的mixin应用创建一个新类,该类出现在AutoStepOperation
的超类链中.
The mixin application of Counter
to Operation
create a new class, and that class appears in the superclass chain of AutoStepOperation
.
如果你做 class X extends Y with I1, I2, I3 { ... }
那么你创建了四个类.如果你只做 class X extends Y implements I1, I2, I3 { ... }
那么你只创建一个类.即使I1
、I2
、I3
都是完全空的抽象接口,使用with
来应用也是等价的到:
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 */ }
你不会直接写那个,所以你也不应该用 with
写它
You wouldn't write that directly, so you shouldn't write it using with
either
这篇关于在 Dart 中何时使用 mixins 以及何时使用接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!