Day 07 | Dart基本介绍 - extends、abstract、mixin

今天继续来说明class 相关的语法。今天提到的语法又会更抽象一点

extends

所谓继承就是我们可以使一个类别使用另外一个类别的方法及成员变数来进行程序码的复用。让我们直接来看code:

//四边形
class Quadrilateral {
  late final leftSide;
  late final topSide;
  late final rightSide;
  late final bottomSide;

  Quadrilateral(this.leftSide, this.topSide, this.rightSide, this.bottomSide) {
    print('\nIn Quadrilateral');
  }

  void showAllSideLength() {
    print('runtimeType(class) :$runtimeType');
    print(
        'leftSide: $leftSide , topSide: $topSide , rightSide: $rightSide , bottomSide:$bottomSide');
  }

  Quadrilateral.twoPairsOfParallelSide(int sideLength1, int sideLengt2) {
    leftSide = sideLength1;
    rightSide = sideLength1;
    topSide = sideLengt2;
    bottomSide = sideLengt2;
  }
}

//正方形
class Square extends Quadrilateral {
  Square(int sideLength)
      : super(sideLength, sideLength, sideLength, sideLength) {
    print('In Square');
  }
 Square.twoPairsOfParallelSide(int sideLength)
      : super.twoPairsOfParallelSide(sideLength, sideLength);
Square.anthorWwoPairsOfParallelSide(int sideLength)
      : super.twoPairsOfParallelSide(sideLength, sideLength);
}

这边我们直接宣告一个extends Quadrilateral 的类别 Square ,继承後我们需要宣告 Square 的 constructor。然後会看到constructor後面接着这个 : super(sideLength, sideLength, sideLength, sideLength)

简单来说 super 就是父类别的意思,所以整行就是Square的constructor 会将传入的 sideLength 分别传入super(也就是Quadrilateral)里的那四个 positional parameter。因为正方形是四边等长的,所以我也只需要传入一个sideLength 就好。

而如果要用父类别的named constructor也可以就只要变成super.namedConstructor 即可。而就算我们在这里新使用的named constructor的是要用父类别的named constructor我们也可以不要用一样的命名,把它想成call function就好。

让我们实际操作看看:

final quadrilateral1 = Quadrilateral(1, 2, 3, 4);
quadrilateral1.showAllSideLength();
final square1 = Square(2);
square1.showAllSideLength();

https://ithelp.ithome.com.tw/upload/images/20210920/20112906da9WdfNhEa.png

但是其实继承的规则有几点:

  1. 每个类别只能继承一个父类别
  2. 如果父类别的default constructor (也就是与class同名的那个)是有参数的,那继承时一定要再次宣告default constructor,如果没有参数就可以不用。
// in  lib
class TestClass {
  late final int a;
  TestClass() {
    print('test');
    a = 0;
  }
}

class TestChild extends TestClass {}

// in bin
final test = TestClass();
print(test);
final testChild = TestChild();
print(testChild);

abstract class & implements

在 dart 里 abstract class 通常是用来实作别的语言中interface(介面)的语法,那 abstract class又是什麽东西,我个人觉得比较像是类别的模板,就像是类别是物件的模板那抽象类可以说是类别的模板。

那为什麽我们需要抽象类?又或者说为什麽我们需要抽象?我自己的观点是:如果在复用程序码时如果写得太死,会变成改A坏B之类的状况,但如果我们将共用的部分抽象了一层,我们既可以复用程序码又可以保持可维护性。

直接看程序码:

abstract class Shape {
  String get name;
  double get perimeter;
}

现在新增一个抽象类别 Shape 来表示所有形状都要有 name及perimeter这两个getter,但为什麽这里的getter不用实作他的回传值呢?这就是所谓的抽象方法,因为在抽象类别(或者可以说是介面)我们不在意他到底如何回传,我们只在乎他回传的形式。

接下来我们要去 Quadrilateral 里实作这些抽象方法

首先我们要先将 Quadrilateral 定义为实作Shape 的类别,在Dart里我们使用的是 implements 这个语法。

class Quadrilateral implements Shape {
//...
@override
  String get name => '$runtimeType';

@override
  double get perimeter => leftSide + topSide + rightSide + bottomSide;
}

然後我们利用 @override 来将这些抽象方法实作出来,但其实即使不使用 @override 也是可以执行,只是在lint中还是会建议你标注 @override ,因为这样子才能一眼看出来哪些是这个class自己的,哪些是从别人来的。

mixin & with

官方文件里是这麽解释mixin的:

Mixins are a way of reusing a class’s code in multiple class hierarchies.

在官方文件来看,mixin最主要就是要拿来复用程序码的。

上面我们讲到了各种复用程序码的方法,使用继承我们可以直接复用父类别的已经实作的功能、使用抽象类别我们可以让多个类别在共用程序码时有一定的规范,但是又可以不用先写死实作。那mixin呢?

简单来说就是:我提供一些方法给你使用,但我不用成为你的父类别。

我们这边先宣告一个 mixin

mixin ShowName {
  void showName() {
    print('mixin ShowName');
    print('runtimeType(class) :$runtimeType');
  }
}

然後在原本的类别上使用 with

class Quadrilateral with ShowName implements Shape {
  //...

  void showAllSideLength() {
    super.showName();
    print(
        'leftSide: $leftSide , topSide: $topSide , rightSide: $rightSide , bottomSide:$bottomSide');
  }

 //...

}

然後我们就能像在使用父类别的方法一样使用super.来使用mixin的方法。


今天的程序码也有上传到github
https://github.com/zxc469469/dart-playground/tree/Day07/extends

这次讲到了许多有关於「程序码复用」、「抽象程序码」这些就算我们不去做也能完成功能的事情,但当我们想要写出一个好维护好扩充的程序时,我们终究得使用这些方法来让我们来达成这些目标。

而对於在开发途中哪些东西该是class、哪些该是abstract class而哪些又要是mixin,也许这类问题在 Design pattern 里会得到解答,但何时使用及该如何使用终究就是考验我们的能力及经验。

再次打个预防针因为我不是一个会写OOP的人,所以对於这些OOP的概念的理解或阐述会有偏差及误解,还期望各位大神可以在底下讨论或指正。

至此对於 Dart 中的 class的相关语法也算是告了一段落,明天会开始进入非同步的介绍。


<<:  人脸辨识的流程--人脸识别

>>:  Day 5:Hexo 安装完成後的设定密技,并且为 Hexo 换上新布景!

时间挤一下就有了,我们挤了没?

前言 利用前几天的篇幅,简单的讨论敏捷与 Scrum,也传达了我觉得团队需要注意的地方。今天再让我们...

DAY14 - 拓扑排序

今天要写拓扑排序~~ 一个有向无环图,必定存在一种(以上)的拓扑排序 定义: 将图中所有点展开成序列...

Rails belong_to

belongs_to 的设定 optional 在 Rails 5.1 之後的版本,belongs_...

OpenPose 安装笔记

Environment python 3.7 cuda 11.5 cudnn 8.3.2.44 cm...

【Day32】[演算法]-内插搜寻法Interpolation Search

内插搜寻法(Interpolation Search  ),又称插补搜寻法,是二分搜寻法的改良版,二...