今天繼續來說明class 相關的語法。今天提到的語法又會更抽象一點
所謂繼承就是我們可以使一個類別使用另外一個類別的方法及成員變數來進行程式碼的復用。讓我們直接來看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();
但是其實繼承的規則有幾點:
// 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);
在 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的:
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
的相關語法也算是告了一段落,明天會開始進入非同步的介紹。