2019独角兽企业重金招聘Python工程师标准>>>
Dart语言集合了现代编程语言的众多优点,Mixin继承机制也是其一。但针对Java程序员来说,可能不是一下子能理解的,比如我第一次看到的时候,也迷迷糊糊了半天——这是啥玩意???
要说Mixin,可能写成MixIn会更好理解,翻译回来就是混入,当然你执意说这是一种“迷信”继承机制,那也没辙。
下面将从一个实际情景入手,对比Java和Dart的实现,以便更好理解Dart的mixin。
场景
我们先来描绘这么一个职业关系图:
职业关系图
从图中可以梳理出以下关系:
工程师类目,有软件工程师和建筑工程师师等,他们共同点都是工程师
教师类目有美术教师,IT教师等,他们共同点都是教师
而以上的工程师、教师都是社会的工作者。
那么接下来我们分别使用Java和Dart来实现这个关系类。
注意:接下来的这篇文章中为了方便表达意义,将所有类写在一个文件里面,请暂时忽略代码不规范细节。并且Dart在本文不会使用语法糖写法。
Java版本实现
从层级出发,可以写出Java版本实现如下:
//工作者
abstract class Worker {
public abstract void doWork();//工作者需要工作
}
//工程师
class Engineer extends Worker {
@Override
public void doWork() {
System.out.println("工程师在工作");
}
}
//教师
class Teacher extends Worker {
@Override
public void doWork() {
System.out.println("教师在教学");
}
}
//软件工程师
class SoftwareEngineer extends Engineer {
}
//建筑工程师
class BuildingEngineer extends Engineer {
}
//美术教师
class ArtTeacher extends Teacher {
}
//IT教师
class ITTeacher extends Teacher {
}
Dart版本实现
//工作者
abstract class Worker {
void doWork();//工作者需要工作
}
//工程师
class Engineer extends Worker {
void doWork() {
print('工程师在工作');
}
}
//教师
class Teacher extends Worker {
void doWork() {
print('教师在教学');
}
}
//软件工程师
class SoftwareEngineer extends Engineer {
}
//建筑工程师
class BuildingEngineer extends Engineer {
}
//美术教师
class ArtTeacher extends Teacher {
}
//IT教师
class ITTeacher extends Teacher {
}
从上面实现可以看出,两个实现并没什么卵区别好咩。。。。。。
嗯,目前来说确实是这样的,因为Dart也是单继承。
因为上面的场景是在是too young too simple了,下面开始扩展一些场景。
场景扩展
还是刚刚那张关系图,我们开始思考这些职业他们都具有什么能力。
于是我给这些职业虚拟了以下能力:
职业
能力
软件工程师
软件设计、修电脑
建筑工程师
手绘
美术教师
手绘、书法
IT教师
修电脑
他们的关系图如下:
通过图形或表格可以看出,软件工程师和IT教师都具备修电脑的能力,建筑工程师和美术教师都具备手绘的能力,但是这些能力都是他们特有的,不是工程师或者教师具备的能力,所以不能在他们的父类中实现。
那么这个时候我们就考虑到一个东西——接口。
以软件工程师和IT教师为例:
他们都具备修电脑的能力:
interface CanFixComputer {
void fixComputer();
}
interface CanDesignSoftware {
void designSoftware();
}
//软件工程师
class SoftwareEngineer extends Engineer implements CanFixComputer, CanDesignSoftware {
@Override
public void fixComputer() {
System.out.println("修电脑");
}
@Override
public void designSoftware() {
System.out.println("设计软件");
}
}
//IT教师
class ITTeacher extends Teacher implements CanFixComputer {
@Override
public void fixComputer() {
System.out.println("修电脑");
}
}
相当标准的实现了
我们知道Dart是没有interface这种东西的,但并不以为着这门语言没有接口,事实上,Dart任何一个类都是接口,你可以实现任何一个类,只需要重写那个类里面的所有具体方法。
我们只需要将上面的interface 修改成 abstract class,就是dart中的实现了。
但是我们发现,fixComputer这个接口被继承了两次,并且两次的实现都是一样的,这里就出现了代码重复和冗余的问题。怎么办呢?在java中我们有接口的default实现来解决这个问题(这是一个java8出现的不得已的方案。)
这个时候mixin的作用就出现了
abstract class CanFixComputer {
void fixComputer() {
print('修电脑');
}
}
abstract class CanDesignSoftware {
void designSoftware() {
print('设计软件');
}
}
//软件工程师
class SoftwareEngineer extends Engineer
with CanFixComputer, CanDesignSoftware {
}
//IT教师
class ITTeacher extends Teacher with CanFixComputer {
}
main() {
ITTeacher itTeacher = new ITTeacher();
itTeacher.doWork();
itTeacher.fixComputer();
SoftwareEngineer softwareEngineer = new SoftwareEngineer();
softwareEngineer.doWork();
softwareEngineer.fixComputer();
softwareEngineer.designSoftware();
}
可以看到,这里不再用implements,更不是extends,而是with。
而且,每个具有某项特性的类不再需要具体去实现同样的功能,接口是没法实现功能的,而通过继承的方式虽然能实现功能,但已经有父类,同时不是一个父类,又不能多继承,所以这个时候,Dart的Mixin机制就比Java的接口会高效,开发上层的只需要关心当前需要什么特性,而开发功能模块的关心具体要实现什么功能。
顺序的理解
既然是with,那应该也会有顺序的区别,
思考一个问题:如果同时with两个类,但两个类中有同样的一个方法的不同实现,那么这个时候应该使用的是哪一个类的方法?
下面以一个简单的Demo来说明这个问题:
class First {
void doPrint() {
print('First');
}
}
class Second {
void doPrint() {
print('Second');
}
}
class Father {
void doPrint() {
print('Father');
}
}
class Son1 extends Father with First,Second {
void doPrint() {
print('Son1');
}
}
class Son2 extends Father with First implements Second {
void doPrint() {
print('Son2');
}
}
main() {
Son1 son1 = new Son1();
son1.doPrint();
Son2 son2 = new Son2();
son2.doPrint();
}
那么这个程序运行后,将会在控制台输出如下:
1
2
Son1
Son2
可以看到,无论是extends、implements还是mixin,优先级最高的是在具体类中的方法。
我们稍微改一下上面的例子:
class First {
void doPrint() {
print('First');
}
}
class Second {
void doPrint() {
print('Second');
}
}
class Father {
void doPrint() {
print('Father');
}
}
class Son1 extends Father with First,Second {
}
class Son2 extends Father with First implements Second {
}
main() {
Son1 son1 = new Son1();
son1.doPrint();
Son2 son2 = new Son2();
son2.doPrint();
}
这个时候控制台输出如下:
1
2
Second
First
可以看到,其实在Son2中implements只是说要实现他的doPrint()方法,这个时候其实具体实现是First中Mixin了具体实现。
而Mixin的具体顺序也是可以从代码倒过来看的,最后mixin的优先级是最高的。
PS: dart中有个关键字 mixin 可以用来替换上面的 class,这个类表示专门用来做mixin的。
另外在mixin类中我们还以使用这样使用
mixin MusicalPerformer on Musician {
// ···
}
这个表示只有类Musician能够使用这个mixin类。