concept

Mixins are classes in object-oriented programming languages, and the relationship between users and mixins is not “IS-A”, but “-able”. This design pattern embodies the principle of dependency inversion. Mixins can achieve the commonality of multiple inheritance in a single inheritance mode, making it easier to reuse code.

Mixin – Wikipedia, the free encyclopedia

The characteristics of

  1. Constructors should not be declared, otherwise there will be syntactic ambiguities when mixing multiple mixins.
  2. Mixed usewithKeyword, can be followed by class,abstract class,mixin
  3. Mixins withonKeyword toqualification

A single mixin

Let’s start with a simple example:

mixin C{
  void run(){}
}

class D with C{}Copy the code

Here are the bytecode files generated by DART:

abstract class C extends core: :Object/ *isMixinDeclaration* /{method the run () - >void {}
  ///Omit irrelevant information
}

abstract class _D&Object&C extends core: :Object implements main: :C/ *isAnonymousMixin.isEliminatedMixin.hasConstConstructor* /{method the run () - >void {}
  ///Omit irrelevant information
}

class D extends main: :_D&Object&C {}Copy the code

Inheritance chain: C< -_d &Object&C< -d

  • Dart first generates an abstract class C’ corresponding to mixin C
  • Generate a bridge class_D&Object&CinheritanceObjectAnd implement C prime
  • D Direct inheritance_D&Object&C

Thus mixins create a chain of inheritance from which code can be reused.

Multiple mixin

The simultaneous use of multiple mixins generates bridge classes in the order in which they appear, so if multiple mixins have the same method, the subclass calls the method using the last mixin. Similarly, if the parent class and mixin have the same methods, the mixin overwrites the parent class’s methods.

Here are the bytecode files generated by DART:

mixin A{
  void run(){}
}

mixin B{
  void run(){}
}

class C with A.B{} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the bytecode -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --abstract class A extends core: :Object/ *isMixinDeclaration* /{}abstract class B extends core: :Object/ *isMixinDeclaration* /{}abstract class **_C&Object&A** extends core: :Object implements main: :A/ *isAnonymousMixin.isEliminatedMixin.hasConstConstructor* /{}abstract class _C&Object&A&B extends main: :_C&Object&A implements main: :B/ *isAnonymousMixin.isEliminatedMixin.hasConstConstructor* /{}class C extends main: :_C&Object&A&B {}Copy the code

qualification

Mixins can qualify conditions, with the keyword on, and can be followed by class, abstract class, or mixin

  • If it is followed by a class or abstract class, only subtypes of that class can be mixed into the mixin
  • If it is followed by a mixin, only subclasses of the mixin can be mixed in. Note the mixing order:
mixin A{}

mixin B on A{}

///Correct term
class C with A.B{}

///Error writing
class C with B.A{}
Copy the code

Here is an example and bytecode:

class Human{
  void run(){}
}

mixin ShuffleDance on Human{}

class Girl extends Human with ShuffleDance{} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the bytecode -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --abstract class Human extends core: :Object {}abstract class ShuffleDance extends main: :Human/ *isMixinDeclaration* /{}abstract class _Girl&Human&ShuffleDance extends main: :Human implements main: :ShuffleDance/ *isAnonymousMixin.isEliminatedMixin* /{}class Girl extends main: :_Girl&Human&ShuffleDance {}Copy the code

Inheritance chain: Human< -shuffledance < -_girl &Human&ShuffleDance< -girl

The helper class generated by mixin ShuffleDance is a subclass of Human, which is where the on keyword comes in

Multiple qualifiers

Multiple bridge classes are generated when there are multiple qualifiers and are generated in the order of inheritance from parent to child.

Generated bytecode files:

class A {
 void run() {
   print("a"); }}mixin B {
 void run() {}
}

///ImplementsA and B before you mix in E
mixin E on A, B {
 void run() {
   print("e"); }}class C extends A with B.E {
 void run() {
   print("c"); }} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the bytecode -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --abstract class A extends core: :Object {}abstract class B extends main: :A/ *isMixinDeclaration* /{}abstract class _E&A&B extends core: :Object implements main: :A.main: :B/ *isAnonymousMixin* /{}abstract class E extends main: :_E&A&B/ *isMixinDeclaration* /{}abstract class _C&Object&A extends core: :Object implements main: :A/ *isAnonymousMixin.isEliminatedMixin.hasConstConstructor* /{}abstract class _C&Object&A&B extends main: :_C&Object&A implements main: :B/ *isAnonymousMixin.isEliminatedMixin.hasConstConstructor* /{}abstract class _C&Object&A&B&E extends main: :_C&Object&A&B implements main: :E/ *isAnonymousMixin.isEliminatedMixin.hasConstConstructor* /{}class C extends main: :_C&Object&A&B&E {}Copy the code

Mixins implement chain calls

When a class is mixed with a qualified mixin, if both mixins happen to have a method in common, the last mixin’s method is called by default:

mixin A{
	void fun(){
		print("a"); }}mixin B on A{
	void fun(){
		print("b"); }}class C with A.B{}Copy the code

C calls fun to print b

Now let’s transform Mixin B

mixin B on A{
	void fun(){
		///To add A
		super.fun();
		print("b"); }}Copy the code

C now calls fun and prints a and B. This chain call makes full use of the dependency of mixin qualifiers and can call interface methods in turn.

Specific application:

  • There’s one FlutterBindingBaseClass, which defines oneinitInstancesInterface methods.
  • WidgetsFlutterBinding, GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBindingThese glue classes all depend onBindingBaseI rewrote itinitInstancesMethods.
  • runApp()These glue classes will be called in turninitInstancesMethod to initialize a set of services associated with the application.