Mixins

Mixins 是什么

Mixin 是一种代码复用方式,可以在互不相关的类继承体系中复用代码,目标是批量为类提供成员实现。有点类似多继承,但是它的目的是给一个类增加一些各种的模块功能。

使用 Mixin

通过关键字 with,后跟一个或多个 Mixin 名称。

class Musician extends Performer with Musical {
  // 代码
}

class Maestro extends Person with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}
Code language: Dart (dart)

定义 Mixin

使用 mixin 关键字声明。

补充:mixin class(Dart 3.0+)可以同时当作普通类、Mixin 使用。

强制约束

  1. mixin / mixin class 不能写 extends
  2. 不能声明任何生成构造函数(generative constructor)
mixin Musical {
  bool canPlayPiano = false;
  bool canCompose = false;
  bool canConduct = false;

  void entertainMe() {
    if (canPlayPiano) {
      print('Playing piano');
    } else if (canConduct) {
      print('Waving hands');
    } else {
      print('Humming to self');
    }
  }
}

class Musician with Musical {}

void main() {
  var m = Musician();
  m.canConduct = true;
  m.entertainMe(); // Waving hands
}
Code language: Dart (dart)

执行结果

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Musician is playing
Musician is playingCode language: CSS (css)

Mixin 需要调用外部成员

Mixin 自身无法定义的方法 / 属性

Mixin 经常需要调用一些自己没有实现的成员,下面提供 4 种标准方案。

在 Mixin 内部定义抽象成员

Mixin 声明抽象方法,所有使用该 Mixin 的类必须实现这个抽象方法

mixin Musician {
  // 抽象方法,使用者必须实现
  void playInstrument(String instrumentName);

  void playPiano() {
    playInstrument('Piano');
  }
  void playFlute() {
    playInstrument('Flute');
  }
}

//使用者必须重写实现
class Virtuoso with Musician {
  @override
  void playInstrument(String instrumentName) {
    print('Plays the $instrumentName beautifully');
  }
}

void main() {
  var artist = Virtuoso();
  artist.playPiano();
  artist.playFlute();
}
Code language: Dart (dart)
Plays the Piano beautifully
Plays the Flute beautifully

通过抽象 Getter 访问宿主类的状态

Mixin 定义抽象 getter,以此读取宿主类的字段,实现跨类状态访问。

/// 可以混入任意带有 name 属性的类,自动提供基于 name 的 == 和 hashCode
mixin NameIdentity {
  String get name;

  @override
  int get hashCode => name.hashCode;

  @override
  bool operator ==(Object other) =>
      other is NameIdentity && name == other.name;
}

class Person with NameIdentity {
  final String name;
  Person(this.name);
}

void main() {
  var p1 = Person("Alice");
  var p2 = Person("Alice");
  var p3 = Person("Bob");
  print(p1 == p2); // true
  print(p1 == p3); // false
}
Code language: JavaScript (javascript)

Mixin 实现接口 implements

Mixin 使用 implements 约束接口,强制混入类补齐接口全部方法。

abstract interface class Tuner {
  void tuneInstrument();
}

mixin Guitarist implements Tuner {
  void playSong() {
    tuneInstrument();
    print('Strums guitar majestically.');
  }
}

class PunkRocker with Guitarist {
  @override
  void tuneInstrument() {
    print("Don't bother, being out of tune is punk rock.");
  }
}

void main() {
  PunkRocker().playSong();
}
Code language: JavaScript (javascript)
Don't bother, being out of tune is punk rock.
Strums guitar majestically.

on 子句:限定 Mixin 只能被特定父类的子类使用

on 作用:限制只有继承自指定类型的类,才能混入当前 Mixin

同时 Mixin 内部可以调用父类的成员、执行 super.xxx()

仅当你需要在 Mixin 内部使用 super 调用父类方法时,才使用 on

class Musician {
  void musicianMethod() {
    print('Playing music!');
  }
}

// 仅能混入 Musician 的子类
mixin MusicalPerformer on Musician {
  void performerMethod() {
    print('Performing music!');
    super.musicianMethod(); // 可以调用被on约束的父类方法
  }
}

// SingerDancer 继承 Musician,因此可以混入 MusicalPerformer
class SingerDancer extends Musician with MusicalPerformer {}

void main() {
  SingerDancer().performerMethod();
}
Code language: Dart (dart)
Performing music!
Playing music!

SingerDancer 混入了MusicalPerformer 所以 可以使用它里面的 performerMethod方法

混入就类似这样,一个类可以把另外一个mixin 里面的方法拿过来,这样自己就可以用了。

⚠️ 错误示范:

如果一个类没有继承 Musician,直接 with MusicalPerformer 会编译报错。


class /mixin/mixin class 区别

  1. mixin:只能通过 with 使用,不能用 extends 继承
  2. class:可以 extends,不能直接当作 mixin 使用
  3. mixin class
    • 既可以用 with 当作 Mixin
    • 也可以用 extends 当作普通父类

约束同样生效:

  • mixin / mixin class 不能写 extends、with
  • class / mixin class 不能写 on
mixin class Musician {
  void play() {
    print("Musician is playing");
  }
}

// 作为 Mixin 使用
class Novice1 with Musician {}

// 作为普通父类继承
class Novice2 extends Musician {}

void main() {
  Novice1().play();
  Novice2().play();
}
Code language: Dart (dart)

运行结果

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Musician is playing
Musician is playing
Code language: CSS (css)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注