js-mixin: https://www.yuque.com/zhuchaoyang/wrif6k/iinaz1#0mtvR
https://resocoder.com/2019/07/21/mixins-in-dart-understand-dart-flutter-fundamentals-tutorial/
没有mixin的世界
以例说明:
假设你在构建一个模拟野生动物的app,那么你需要一个Mosquito(蚊子)类。你会抽象蚊子们有的共通的东西然后放在一个抽象类里。
abstract class Insect {void crawl() {print('爬行');}}abstract class AirborneInsect extends Insect {void fly() {print('飞行');}void buzz() {print('嗡嗡响');}}class Mosquito extends AirborneInsect {void doMosquitoThing() {crawl();fly();buzz();print('吸血');}}
添加新的昆虫,根本不会有代码的冗余出现。
一直到你发现你需要一个Swallow类(就是一种可以吃掉整个蚊子的东西)。
同样的也有很多鸟类共有的东西,我们可以创建一个Bird类。这个时候问题就出现了,鸟也会振动翅膀!但是,你没法把fly方法从AirboneInsect类里面提取出来组成一个新的类Flying。
为什么?Bird类可以继承Flying类,但是AirboneInsect不可以,它已经继承了Insect类了。Dart可不支持多继承。
这下,你需要给Bird类添加一个fly方法了。代码冗余发生了!
abstract class Bird {void chirp() {print('鸣叫');}// 冗余的方法void fly() {print('飞行');}}class Swallow extends Bird {void doSwallowThing() {chirp();fly();print('吃蚊子');}}
使用mixins
在经典的面向对象编程语言里一定会有常规的类,抽象类和接口。
mixins 的中文意思是混入,就是在类中混入其他功能。Mixins的定义是“一种把类代码用在多个继承树的方法”。即:它是一种在多个类层次结构中复用类代码的方法,简单的说,就是可以允许你通过非继承的方式来复用类中的代码。
通常,只要一个类是继承自Object的而且没有定义构造方法,那么这个类可以是一个Mixin了。当然,如果你想让mixin的定义更加的清晰,可以使用mixin关键字开头来定义。具体请参考这里。
因为mixins使用的条件,随着Dart版本一直在变,这里讲的是Dart2.1中使用mixins的条件:
- mixins类只能继承自object
- mixins类不能有构造函数
- 一个类可以mixins多个mixins类
- 可以mixins多个类,不破坏Flutter的单继承
mixins可以用在常规的类上面也可以用在抽象类,只需要一个with关键字。
怎么来理解with呢?
继承 -> extends
mixins -> with
**
继承和mixins是一样的,是语言的特性,with 和 extends 是关键字。
最简单的mixin
mixin本身可以是抽象的,可以定义各种方法属性,也可以是抽象的,等后续类去实现
mixin A {int age = 1;run() {print('A -- run');}init();}class Person with A {// A实例的init方法没有方法体,Person必须要实现,否则报错@overrideinit() {print('Person -- $age');print('Person -- init');}}void main() {var p = Person();p.run(); //A -- runprint(p.age); //1p.init();// Person -- 1// Person -- init}
同名覆盖顺序问题
class A {log() => print('A');}mixin B {log() => print('B');}class Base {log() => print('Base');}class Demo1 extends Base {}class Demo2 extends Base with A, B {}class Demo3 extends Base with B, A {}class Demo4 extends Base with A, B {@overridelog() => print('Demo');}// Demo4 -> B -> A -> Basevoid main() {Demo1().log(); //BaseDemo2().log(); //BDemo3().log(); //ADemo4().log(); //Demo}
基于某个类型的mixin
当mixin使用 on 关键字基于某类型时,则该mixin只能在这个类的子类中使用。
mixin中自然也可以调用这个类定义的方法、属性。
class Base {log() {print('Base -- log');}}mixin A on Base {int age = 1;run() {print('A -- run');}init() {log();}}// A 必须是 Base 的子类,才可以混入Test// class Person with A{} //报错class Person extends Base with A {}void main() {var p = Person();p.run(); //A -- runprint(p.age); //1p.init();// Base -- log}
多个mixin
如果mixin存在冲突的部分,后面会覆盖前面的,没有冲突的则会保留,所以可以存在后面的mixin修改了前面的mixin的一部分逻辑的情况,不需要直接继承即可实现覆盖,避免了更复杂的继承关系。
mixin A {int age = 1;run() {print('A -- run');}init();}mixin B {int age = 2;close() {print('B -- close');}}class Person with A, B {@overrideinit() {print('Person -- init');}}void main() {var p = Person();p.run(); //A -- runprint(p.age); //2p.init(); //Person -- initp.close(); //B -- close}
多重继承
import 'dart:developer';class Base {init() {print('Base init');}Base() {print('Base 构造函数开始');init();print('Base 构造函数结束');}}mixin A on Base {init() {print('A init start');super.init();print('A init end');}}mixin B on Base {init() {print('B init start');super.init();print('B init end');}}class Person extends Base with A, B {@overrideinit() {print('Person init start');super.init();print('Person init end');}}void main() {var p = Person();// Base 构造函数开始// Person init start// B init start// A init start// Base init// A init end// B init end// Person init end// Base 构造函数结束}
前文模拟野生动物的app示例
mixin可以用在常规的类上面也可以用在抽象类,只需要一个with关键字。在野生动物模拟app例子里,你也许要用在抽象类上了。
//飞行特性mixin Flying {void fly() {print('flying');}}//昆虫类abstract class Insect {void crawl() {print('crawling');}}//飞行昆虫类abstract class AirborneInsect extends Insect with Flying {void buzz() {print('buzzing annoyingly');}}//鸟类abstract class Bird with Flying {void chirp() {print('chirping');}}
//蚊子class Mosquito extends AirborneInsect {void doMosquitoThing() {crawl();fly();buzz();print('sucking blood');}}//吃蚊子的某种生物class Swallow extends Bird {void doSwallowThing() {chirp();fly();print('eating a mosquito');}}void main() {Mosquito().doMosquitoThing(); //crawling -> flying -> buzzing annoyingly -> sucking bloodSwallow().doSwallowThing(); //chirping -> flying -> eating a mosquito}
也可以在一个类上面使用多个mixin。如果需要在Bird类上用一个chirping mixn的话,就和Flying mixin一起用就好。
abstract class Bird with Flying, Chirping
结论
Mixin对于继承体系中避免代码的冗余非常有用处。mixin也可以被约束在只可以用在某些特定的类上面,这让他们成为了开发的一大利器!
