在 Dart 中所有東西都是一個物件 - 每一個儲存在變數的「值」都是某個類別的物件實例。所有物件都繼承自 Object 類別。這種設計使 Dart 成為一種純粹的物件導向程式語言。
要建立一個類別的物件(實例化),必須使用一個特殊的方法稱為建構子(constructor)。
int float 等都是物件且繼承自 Object 類別。表示它們是可以被覆寫修改行為。class Point {
  double x, y;
  
  Point(this.x, this.y); // 建構子簡化語法糖
  // 非簡化版本
  // Point(double x, double y) {
  //   this.x = x;
  //   this.y = y;
  // }
  
  Point.origin(): x = 0, y = 0; // 具名建構子
}
void main() {
  var p1 = Point(1, 2);
  var p2 = Point.origin();
}
除了類別之外,Dart 也包含其他物件導向的智慧
封裝是物件導向程式設計的重要原則之一,它讓我們可以隱藏實作細節,只揭露必要的介面。
實務上,Dart 的封裝是在函式庫級別實現的,而不是類別級別。
Dart 沒有明確指定欄位或方法存取範圍的功能,像 Java 有 protected,private,public 等。Dart 會為全部類別中的欄位建立隱式的 getter 和 setter,因此你可以通過這些 getter 和 setter 控制類別的屬性。
class Person {
  String name;
  int _age;
  Person(this.name, this._age);
}
void main() {
  var person = Person('Alice', 20);
  print(person.name); // 可
  print(person._age); // 如果不在同一檔案,不能直接訪問 _age,因為它是私有的。但是同一個庫/檔案是可以的。
}
在 Dart 如果一個類別,類別成員,全域函式或者變數使用 _ 開頭的話就是專屬於該函式庫(私有)。
關於私有屬於檔案層級的範例:
class Person {
  String name;
  int _age;
  
  Person(this.name, this._age);
}
class Employee {
  void age(Person person) {
    print(person._age); // 當在同一個檔案的時候可以存取
  }
}
main() {
  var person = Person('Alice', 20);
  var employee = Employee();
  employee.age(person); // 可以使用 _age
}
繼承可以讓我們擴展一個類別的定義。
mixins 可以擴展類別的功能而不依賴繼承。extends 關鍵字定義繼承。final
final class Point {
  final double x;
  final double y;
  
  Point(this.x, this.y);
}
標記 final 表示該類別不能被繼承。類別中的成員 x 和 y 也被標記為 final,這表示這些成員一旦被初始化後,它們的值就不能被改變。
繼承外,還有抽象類別用來定義類別的特徵,而不直接實作。
implements 而不是繼承interface 類別修飾子class Car {
  void drive() {
    //
  }
}
// 實作類別介面
class ElectricCar implements Car {
  @override
  void drive() {}
}
abstract class Vehicle {
  void drive();
}
class Car implements Vehicle {
  @override
  void drive() {}
}
  
interface IVehicle {
  void drive();
}
class ElectricCar implements IVehicle {
  @override
  void drive() {}
}
當你想要保留某個類別的功能進而擴展時可以使用繼承 extends,一個類別只能繼承一個父類別;單一繼承
而實現 implements 介面則是用來強制某個類別遵守某個規範,實現介面不會繼承任何已經實作的程式碼,必須自己實現。
抽象類別和介面的差異:抽象類別可以包含局部實作和局部規範,可共享程式碼。
至於介面只能定義方法簽章。
- 繼承:只能單一繼承表示 is-a 的關係,通過
extends實現,子類別會獲得父類別的功能可以直接使用或覆寫- 介面:是一種表示 behaves-as 的關係,在 Dart 類別也可以作為介面通過
implements語法實現。一個類別可以實現多個介面,必須提供介面宣告的全部方法- 混入(mixin):使用
with實現,用於共用方法,不適合單一繼承或介面規範重複一樣的實作。class Vehicle { void drive() { print('move'); } } class Car implements Vehicle { @override void drive() { print('move with gas'); } } class ElectricCar extends Car { @override void drive() { print('move silently'); } } mixin AutoDriving { void navigate() { print('navigating'); } } class Tesla extends ElectricCar with AutoDriving { @override void drive() { super.drive(); navigate(); } }
Mixin 是 Dart 中重用類別代碼的一種方式,不需要複雜的繼承層次:
mixin Swimmable {
  void swim() {
    print('Swimming');
  }
}
class Duck extends Animal with Swimmable {}
多型簡單的說就是物件允許被視為父類別的一種物件實例,可視為一個物件表現的像另一個物件的能力例如 int 也是一種 num。
通過覆寫方法,子類可以改變父類同名方法的行為,也就是在呼叫的時候可視為同一個動作,但是具體實現不一樣。
另外則是要注意:Dart 不支援方法重載,你不能用不同參數定義兩個同名的方法,如果有這種需求嘗試使用可選,位置,具名參數的方式來實現,也就是同一個方法支援各種參數。
會提到不支援方法重載 overload 的重點在於我們可以通過例如可選參數的方式,在維持一樣參數的情況下達成各種需要的覆寫。
物件導向的三大特性:封裝、繼承、多型(多態)
public private 這些實作,Dart 是檔案層級。move 方法但是細節卻不同,一個變數可以 Animal a = Dog() 如此一來變數都可以執行 a.move() 卻有不同的表現形式。Dart 稱為真物件導向語言,在 Dart 中函式也是物件,意味著:
這被稱為 first-class functions 因為它們可以和其他的型別一樣,並且在 Flutter 中常被用來建立 Widget。
Dart 的物件導向特性提供了強大而靈活的工具來組織和構建你的代碼。
隨著你在 Flutter 開發中的深入,你會發現這些物件導向的概念如何幫助你構建複雜的 UI 和管理應用狀態。如果是第一次從腳本類型語言踏進物件導向的人來說,Dart 的類別結構和語法可能會顯得繁瑣和複雜。確實可能帶來一定的學習曲線。然而,儘管初期可能感到困難,但掌握 OOP 的概念和技巧將可以提升程式的可維護性、可重用性和擴展性。