首页 > 其他分享 >Dart mixin 的一些理解

Dart mixin 的一些理解

时间:2023-12-26 11:39:05浏览次数:31  
标签:initInstances Flutter void BindingBase 理解 mixin Dart class


本文主要介绍关于 Dart mixin 的一些理解。理解 mixin 概念的关键在于理解中间类。

Mixins are a way of reusing code in multiple class hierarchies

先来看一个简单例子:

class Piloted {
  int astronauts = 1;
  void describeCrew() {
    print('Number of astronauts: $astronauts');
  }
}

class PilotedCraft extends Spacecraft with Piloted {
  // ···
}

PilotedCraft 拥有 astronauts 字段和 describeCrew() 方法。

mixin 是什么?

维基百科中这样定义 mixin:

In object-oriented programming languages, a Mixin is a class that contains methods for use by other classes without having to be the parent class of those other classes.

即,mixin 是另外一个普通类,我们可以在不继承这个类的情况下从这个类”借用”方法和变量。

Support for the mixin keyword was introduced in Dart 2.1. Code in earlier releases usually used abstract class instead.

从这个角度来讲,mixin 不过是 abstract class

Java tries to make up for this by using Interfaces, but that is not as useful or flexible as mixins.

从这个角度来讲,可以认为 mixin 是带实现的接口。

小节

  • mixin 有点类似 abstract class
  • mixin 有点类似 interface
  • 不能继承 mixin
  • 可以使用 mixin, abstract class, class 来作为 mixin

如何使用 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;
  }
}

实现 mixin 的方法同样也很简单:创建一个继承自 Object 的类并且不要声明构造方法。如果想让 mixin 作为普通类使用,使用 class 关键字;如果不想让 mixin 作为普通类使用,使用 mixin 关键字代替 class

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');
    }
  }
}

on 的用法

The keyword on is used to restrict our mixin’s use to only classes which either extends or implements the class it is declared on. In order to use the on keyword, you must declare your mixin using the mixin keyword

class B {}
mixin Y on B {
  void hi() {
    print('hi');
  }
}
class Q with Y {}

则有如下错误提示:

Error: 'Object' doesn't implement 'B' so it can't be used with 'Y'.

on 关键字限制了 Y 的使用范围:Y 只能用于继承或实现了 B 的类。修复方式是让 Q 继承自 B

class Q extends B with Y {}

mixin 解决了什么问题?

mixin 解决了多重继承中的 Deadly Diamond of Death(DDD) 问题。

多重继承问题简单描述。各个类的继承关系如下:

Dart mixin 的一些理解_设计模式

mixin 与继承 -w500

class Performer {
    abstract void perform();
}

class Dancer extends Performer {
    void perform() {}
}

class Singer extends Performer {
    void perform() {}
}

class Musician extends Dancer, Singer {
}

问题来了,当调用 Musician.perform() 时,到底会调用哪个 **perform()** 方法是模糊的

来看 mixin 如何解决这个问题。见 Dart for Flutter : Mixins in Dart - Flutter Community - Medium

class Performer {
    abstract void perform();
}

mixin Dancer {
    void perform() {}
}

mixin Singer {
    void perform() {}
}

class Musician extends Performer with Dancer, Singer {
}

现在,当调用 Musician.perform() 时,到底会调用哪个 **perform()** 方法是确定的。在这里是调用 Singer.perform()

mixin 有一套明确的机制来选择调用哪个方法。

Dart mixin 的一些理解_代理模式_02

minx 是线性的栈结构 -w500

假设 Musician 类使用多个 mixin (Dancer, Singer)。该类有个方法名为 perform()Musician 类继承自 Performer 类。

  • 首先,将 Performer 类置于栈顶
  • 其次,后声明的 mixin 优先于后声明的 mixin。按顺序将 mixin 置于栈中,在这里分别是 Dancer, Singer
  • 最后,将 Musician 类自己置于栈中。Musician 类中的 perform() 被优先调用

Dart 使用的是单重继承 (Java 也是单重继承,C++ 是多重继承)。多重继承更为强大,但会引起 Deadly Diamond of Death(DDD) 问题。

Java 使用接口(interface)来部分实现多重继承。多重继承的问题是需要在每个类中实现接口(interface),所以并不是一个好的方案。(实际上 Java 已经通过默认方法修复了这个问题)

所以 Dart 中就有了 mixin。

理解 mixin

Mixins in Dart work by creating a new class that layers the implementation of the mixin on top of a superclass to create a new class — it is not “on the side” but “on top” of the superclass, so there is no ambiguity in how to resolve lookups. Mixins is not a way to get multiple inheritance in the classical sense. Mixins is a way to abstract and reuse a family of operations and state. It is similar to the reuse you get from extending a class, but it is compatible with single-inheritance because it is linear. StackOverflow

class A {
  String getMessage() => 'A';
}

class B {
  String getMessage() => 'B';
}

class P {
  String getMessage() => 'P';
}

class AB extends P with A, B {}

class BA extends P with B, A {}

void main() {
  String result = '';

  AB ab = AB();
  result += ab.getMessage();

  BA ba = BA();
  result += ba.getMessage();

  print(result);
}

以上这段代码输出 BA

从语义上讲以上这段代码等同于:

class PA = P with A;
class PAB = PA with B;

class AB extends PAB {}

class PB = P with B;
class PBA = PB with A;

class BA extends PBA {}

继承结构图是这样的:

Dart mixin 的一些理解_代理模式_03

mixin 的继承关系 -w400

Since each mixin application creates a new class, it also creates a new interface (because all Dart classes also define interfaces). As described, the new class extends the superclass and includes copies of the mixin class members, but it also implements the mixin class interface. In most cases, there is no way to refer to that mixin-application class or its interface; the class for Super with Mixin is just an anonymous superclass of the class declared like class C extends Super with Mixin {}. If you name a mixin application like class CSuper = Super with Mixin {}, then you can refer to the mixin application class and its interface, and it will be a sub-type of both Super and Mixin.

理解 mixin 的关键在于它是线性的。

使用场景

  • 在没有共同父类的各个类中共享代码时
  • 在父类中实现某种方法无意义时

源码分析

(2020-7-22 更新:从mixin机制理解Flutter App启动 - 也是分析 Flutter App 启动过程时无法理解 mixin,然后逐步了解 mixin 概念,再回过头来看 Flutter App 启动过程)

runApp() 是我们运行应用的入口。这个方法的代码如下:

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..attachRootWidget(app)
    ..scheduleWarmUpFrame();
}

这里的的 WidgetsFlutterBinding 联系着 Flutter framework 和 Flutter engine。

A concrete binding for applications based on the Widgets framework. This is the glue that binds the framework to the Flutter engine.

WidgetsFlutterBinding 定义如下:

class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, 
 SchedulerBinding, PaintingBinding, SemanticsBinding,
 RendererBinding, WidgetsBinding {
}

abstract class BindingBase {
}

mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, HitTestTarget {}

mixin ServicesBinding on BindingBase {}

mixin SchedulerBinding on BindingBase, ServicesBinding {}

mixin PaintingBinding on BindingBase, ServicesBinding {}

mixin SemanticsBinding on BindingBase {}

mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {}

abstract class HitTestable {}
  • 定义 mixin。mixin RendererBinding 定义了一个名为 RendererBinding 的 mixin
  • 限制 mixin 的使用范围。mixin RendererBinding on BindingBase, ServicesBinding 限制 RendererBinding 只能用于继承或实现了 BindingBaseServicesBinding 的类上
  • 使用 mixin。class WidgetsFlutterBinding extends BindingBase with RendererBinding
class WidgetsFlutterBinding extends BindingBase
    with
        GestureBinding,
        ServicesBinding,
        SchedulerBinding,
        PaintingBinding,
        SemanticsBinding,
        RendererBinding,
        WidgetsBinding {}

abstract class BindingBase {
  void initInstances() {
    print("BindingBase.initInstances()");
  }

  BindingBase() {
    initInstances();
  }
}

mixin WidgetsBinding
    on
        BindingBase,
        SchedulerBinding,
        GestureBinding,
        RendererBinding,
        SemanticsBinding {
  static WidgetsBinding _instance;

  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    print('WidgetsBinding.initInstances() $this');
  }
}

mixin GestureBinding on BindingBase implements HitTestable {
  static GestureBinding _instance;

  void initInstances() {
    super.initInstances();
    _instance = this;
    print('GestureBinding.initInstances() $this');
  }
}

mixin ServicesBinding on BindingBase {
  static ServicesBinding _instance;

  void initInstances() {
    super.initInstances();
    _instance = this;
    print('ServicesBinding.initInstances() $this');
  }
}

mixin SchedulerBinding on BindingBase, ServicesBinding {
  static SchedulerBinding _instance;

  void initInstances() {
    super.initInstances();
    _instance = this;
    print('SchedulerBinding.initInstances() $this');
  }
}

mixin PaintingBinding on BindingBase, ServicesBinding {
  static PaintingBinding _instance;

  void initInstances() {
    super.initInstances();
    _instance = this;
    print('PaintingBinding.initInstances() $this');
  }
}

mixin SemanticsBinding on BindingBase {
  static SemanticsBinding _instance;

  void initInstances() {
    super.initInstances();
    _instance = this;
    print('SemanticsBinding.initInstances() $this');
  }
}

mixin RendererBinding
    on
        BindingBase,
        ServicesBinding,
        SchedulerBinding,
        GestureBinding,
        SemanticsBinding,
        HitTestable {
  static RendererBinding _instance;

  void initInstances() {
    super.initInstances();
    _instance = this;
    print('RendererBinding.initInstances() $this');
  }
}

abstract class HitTestable {}

main(List<String> args) {
  WidgetsFlutterBinding b = WidgetsFlutterBinding();
  print('main(): $b');
}

这段代码输出为:

BindingBase.initInstances()
GestureBinding.initInstances() Instance of 'WidgetsFlutterBinding'
ServicesBinding.initInstances() Instance of 'WidgetsFlutterBinding'
SchedulerBinding.initInstances() Instance of 'WidgetsFlutterBinding'
PaintingBinding.initInstances() Instance of 'WidgetsFlutterBinding'
SemanticsBinding.initInstances() Instance of 'WidgetsFlutterBinding'
RendererBinding.initInstances() Instance of 'WidgetsFlutterBinding'
WidgetsBinding.initInstances() Instance of 'WidgetsFlutterBinding'
main(): Instance of 'WidgetsFlutterBinding'

可以这样理解,WidgetsFlutterBinding 并不是继承自 BindingBase,而是继承自如下一个匿名类:

class BindingBase with
        GestureBinding,
        ServicesBinding,
        SchedulerBinding,
        PaintingBinding,
        SemanticsBinding,
        RendererBinding,
        WidgetsBinding

最后

这里也为想要学习Flutter的朋友们准备了两份学习资料《Flutter Dart语言编程入门到精通》《Flutter实战》,从编程语言到项目实战,一条龙服务!!

《Flutter Dart 语言编程入门到精通》

  • 第一章 Dart语言基础
  • 第二章 Dart 异步编程
  • Dart mixin 的一些理解_android_04

  • 第三章 异步之 Stream 详解
  • 第四章 Dart标准输入输出流
  • Dart mixin 的一些理解_设计模式_05

  • 第五章 Dart 网络编程
  • 第六章 Flutter 爬虫与服务端
  • Dart mixin 的一些理解_笔记_06

  • 第七章 Dart 的服务端开发
  • 第八章 Dart 调用C语言混合编程
  • 第九章 LuaDardo中Dart与Lua的相互调用
  • Dart mixin 的一些理解_代理模式_07

《Flutter实战:第二版》

  • 第一章:起步
  • 第二章:第一个Flutter应用
  • 第三章:基础组件
  • 第四章:布局类组件
  • 第五章:容器类组件

Dart mixin 的一些理解_系统安全_08

  • 第六章:可滚动组件
  • 第七章:功能型组件
  • 第八章:事件处理与通知
  • 第九章:动画
  • 第十章:自定义组件
  • Dart mixin 的一些理解_代理模式_09

  • 第十一章:文件操作与网络请求
  • 第十二章:Flutter扩展
  • 第十三章:国际化
  • 第十四章:Flutter核心原理
  • 第十五章:一个完整的Flutter应用
  • Dart mixin 的一些理解_系统安全_10

标签:initInstances,Flutter,void,BindingBase,理解,mixin,Dart,class
From: https://blog.51cto.com/u_16163480/8980464

相关文章

  • Dart设计模式之代理模式
    dart设计模式之代理模式代理模式模式分析在代理模式(ProxyPattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。模式难点模式解决问题在直接访问对象时带来的问题,比如说:要访问的对象在远程的机......
  • Dart设计模式之享元模式
    dart设计模式之享元模式享元模式(Flyweight)模式分析享元模式(FlyweightPattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。享元模式尝试重用现有的同类对象,如果未找到匹配的对......
  • 【电压和电流基础知识学习理解】
    电压:表示电路中某两个节点的电势差(电场中两点之间电势的差值)。电流:表示单位时间内通过导体的电荷的量,电荷定向移动形成电流(从高电势流向低电势)。电压电流关系:有了电压,才有可能产生电流,但是有电压,未必就会产生电流。电压必须加在导体的两端,这样导体中才会产生电流,如果加在......
  • 神经网络优化篇:如何理解 dropout(Understanding Dropout)
    理解dropoutDropout可以随机删除网络中的神经单元,为什么可以通过正则化发挥如此大的作用呢?直观上理解:不要依赖于任何一个特征,因为该单元的输入可能随时被清除,因此该单元通过这种方式传播下去,并为单元的四个输入增加一点权重,通过传播所有权重,dropout将产生收缩权重的平方范数的......
  • 深入理解 Docker 核心原理:Namespace、Cgroups 和 Rootfs
    通过这篇文章你可以了解到Docker容器的核心实现原理,包括Namespace、Cgroups、Rootfs等三个核心功能。如果你对云原生技术充满好奇,想要深入了解更多相关的文章和资讯,欢迎关注微信公众号。搜索公众号【探索云原生】即可订阅后续文章会演示如何从零实现一个简易的Docker,......
  • STM32实战之深入理解I²C通信协议
    IIC(Inter-IntegratedCircuit),也称为I²C或TWI(Two-WireInterface),是一种广泛使用的串行总线接口,用于连接低速度的集成电路。这种通信协议非常适合在单个主设备和多个从设备之间进行短距离通信。I²C的物理层IIC通信只需要两根线:一个是串行数据线(SDA),另一个是串行时钟线(SCL)。这两根线......
  • 自然语言处理的新星:生成模型在语言理解中的突破
    1.背景介绍自然语言处理(NLP)是人工智能领域的一个重要分支,旨在让计算机理解、生成和处理人类语言。在过去的几十年里,NLP研究主要集中在语言模型、语义分析、情感分析、机器翻译等方面。然而,直到2010年代,随着深度学习技术的诞生,NLP领域遭到了深度学习技术的洗礼,这一时期被称为“深度......
  • 深入理解 Spring IoC 和 DI:掌握控制反转和依赖注入的精髓
    在本文中,我们将介绍IoC(控制反转)和DI(依赖注入)的概念,以及如何在Spring框架中实现它们。什么是控制反转?控制反转是软件工程中的一个原则,它将对象或程序的某些部分的控制权转移给容器或框架。我们最常在面向对象编程的上下文中使用它。与传统编程相比,传统编程中我们的自定义......
  • Java多线程:深入理解Java中的死锁
    一、引言死锁是计算机科学中的一个重要概念,特别是在并发编程中。在Java中,死锁是指两个或更多的线程永久地等待对方释放资源的情况。当两个或更多的线程无限期地等待对方释放锁定的资源时,就会发生死锁。本文将通过示例和深入分析,探讨Java中的死锁问题。二、示例:银行家问题为了更好地......
  • 从容器的发展历史理解容器的本质
    本文分享自华为云社区《容器化学习——从容器的发展历史理解容器的本质》,作者:breakDawn。近期工作上开始接触了相关容器化的内容,因此整理学习了一堆有关容器化的知识,特此进行分享。首先,理解K8S和容器,首先需要学习以下它的发展历史,才能逐步理解容器的意义和作用。阶段一:隔离文件—......