文档章节

理解Dart的Mixin继承机制

街角的小丑
 街角的小丑
发布于 2018/11/28 17:41
字数 1733
阅读 1103
收藏 0

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类。

本文转载自:http://kevinwu.cn/p/ae2ce64/#%E5%9C%BA%E6%99%AF

街角的小丑
粉丝 8
博文 118
码字总数 217248
作品 0
杭州
私信 提问
一个前端码农的 Flutter 实战经验

前言 当年React Native 正火的时候,我撸了一个一席的客户端,最近抽空把我自己的项目用Flutter 写一下,项目地址戳这里,走过路过随手给个star🌟,不胜感激; 以下是作为前端对Flutter 的...

evont
03/14
0
0
Flutter | 记一次Mixin Analysis异常

问题描述 今天在更新了AndroidStudio后出现了异常,所有的Mixin都被分析器指出了错误。报错信息如下: The class 'SingleTickerProviderStateMixin' can't be used as a mixin because it e......

Vadaski
03/05
108
0
Flutter 知识梳理 (Dart) - implements, extends, mixin 的理解

一、前言 在使用语言设计类之间关系的时候,我们会接触到 组成单元 和 关系连接 这两类概念: 组成单元:普通类、抽象类,接口。 关系连接:实现,继承。 而在当中,对于这两类概念进行了增减...

泽毛
07/09
0
0
Dart的语法详解系列篇(二)-- 类与函数

版权声明:本文为博主原创文章,未经博主允许不得转载。https://www.jianshu.com/p/44ae73a58ebc 转载请标明出处: https://www.jianshu.com/p/44ae73a58ebc 本文出自 AWeiLoveAndroid的博客...

AWeiLoveAndroid
2018/12/30
0
0
Dart 要想飞的高基础要打牢篇三(类、泛型、库)

介绍 Dart 要想飞的高基础要打牢篇一(变量与操作符) Dart 要想飞的高基础要打牢篇二(函数) 本片文章有点长,主要讲了 Dart 中 类、泛型和库 几个重要的概念。请耐心看下去。并没有给出过多的...

praise
07/31
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周日乱弹 —— 我,小小编辑,食人族酋长

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @宇辰OSC :分享娃娃的单曲《飘洋过海来看你》: #今日歌曲推荐# 《飘洋过海来看你》- 娃娃 手机党少年们想听歌,请使劲儿戳(这里) @宇辰OSC...

小小编辑
今天
533
10
MongoDB系列-- SpringBoot 中对 MongoDB 的 基本操作

SpringBoot 中对 MongoDB 的 基本操作 Database 库的创建 首先 在MongoDB 操作客户端 Robo 3T 中 创建数据库: 增加用户User: 创建 Collections 集合(类似mysql 中的 表): 后面我们大部分都...

TcWong
今天
31
0
spring cloud

一、从面试题入手 1.1、什么事微服务 1.2、微服务之间如何独立通讯的 1.3、springCloud和Dubbo有哪些区别 1.通信机制:DUbbo基于RPC远程过程调用;微服务cloud基于http restFUL API 1.4、spr...

榴莲黑芝麻糊
今天
16
0
Executor线程池原理与源码解读

线程池为线程生命周期的开销和资源不足问题提供了解决方 案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。 线程实现方式 Thread、Runnable、Callable //实现Runnable接口的...

小强的进阶之路
昨天
47
0
maven 环境隔离

解决问题 即 在 resource 文件夹下面 ,新增对应的资源配置文件夹,对应 开发,测试,生产的不同的配置内容 <resources> <resource> <directory>src/main/resources.${deplo......

之渊
昨天
56
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部