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必须要实现,否则报错
@override
init() {
print('Person -- $age');
print('Person -- init');
}
}
void main() {
var p = Person();
p.run(); //A -- run
print(p.age); //1
p.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 {
@override
log() => print('Demo');
}
// Demo4 -> B -> A -> Base
void main() {
Demo1().log(); //Base
Demo2().log(); //B
Demo3().log(); //A
Demo4().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 -- run
print(p.age); //1
p.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 {
@override
init() {
print('Person -- init');
}
}
void main() {
var p = Person();
p.run(); //A -- run
print(p.age); //2
p.init(); //Person -- init
p.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 {
@override
init() {
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 blood
Swallow().doSwallowThing(); //chirping -> flying -> eating a mosquito
}
也可以在一个类上面使用多个mixin。如果需要在Bird类上用一个chirping mixn的话,就和Flying mixin一起用就好。
abstract class Bird with Flying, Chirping
结论
Mixin对于继承体系中避免代码的冗余非常有用处。mixin也可以被约束在只可以用在某些特定的类上面,这让他们成为了开发的一大利器!