深入淺出:探討 Dart 和 Flutter 中的面向物件程式設計


摘要

本文深入淺出地探討 Dart 和 Flutter 中的面向物件程式設計,揭示這些技術如何優化開發體驗並提高應用效率。 歸納要點:

  • 探討 Flutter 的物件導向設計,包括其強大的 State Management 和 Widget Tree 架構。
  • 介紹 Dart 語言中的 Pattern Matching 和 Null Safety 功能,提升程式碼精簡度及安全性。
  • 解析繼承、過載、Mixin 和 Extension 方法在 Flutter 中的應用與未來趨勢。
透過本文章,你將全面了解 Flutter 的物件導向架構、Dart 語言的關鍵功能及其在實際專案中的應用價值。

物件導向程式設計(Object-Oriented Programming, OOP)是一種將軟體設計圍繞在物件及其互動之上的正規化。作為 Flutter 背後的語言,Dart 完全支援 OOP 原則,使其成為構建健壯且可擴充套件應用程式的絕佳選擇。在這份全面指南中,我們將深入探討 OOP 的四大支柱——封裝、繼承、多型和抽象,以及像方法覆寫和多載等概念,所有這些都會在 Dart 和 Flutter 的上下文中進行說明。

我們在研究許多文章後,彙整重點如下
網路文章觀點與我們總結
  • 物件導向程式設計(OOP)的三大特性是封裝、繼承和多型。
  • 封裝(Encapsulation)指的是將相關的屬性和方法視為一個單位或物件,並隱藏其實作細節。
  • 繼承(Inheritance)允許新類別基於現有類別進行擴展,從而重複使用代碼。
  • 多型(Polymorphism)讓不同的物件可以以相同的方式被操作,即使它們內部實作不同。
  • OOP不僅提升了代碼的可讀性和維護性,也提高了開發效率。
  • Visual Basic等語言全面支援OOP,提供更靈活與強大的程式設計能力。

物件導向程式設計(OOP)的精髓在於透過封裝、繼承和多型這三大特性來模擬現實世界。封裝讓我們能夠隱藏內部細節,只需了解如何使用即可;繼承則讓我們可以基於已有功能進行擴展,大幅減少重複代碼;多型則賦予系統更多彈性,使得不同對象可以共享接口。這些特性能提升開發效率及代碼品質,讓我們更能專注於解決問題,而非糾結於技術細節。

觀點延伸比較:
特性Dart 支援情況Flutter 支援情況最新趨勢權威觀點
封裝(Encapsulation)完全支援,使用私有字段與公共方法進行封裝。完全支援,利用 StatefulWidget 和 StatelessWidget 實現封裝。強調使用 Immutable Widget 增加應用穩定性和可測試性。Google 開發者建議在開發大型應用時,更多地運用封裝以提高代碼的維護性。
繼承(Inheritance)支持類別繼承,允許子類擴展父類功能。通過繼承自 Widget 類來構建複雜 UI 元素。組合優於繼承的設計原則逐漸流行,以減少耦合度和提高靈活性。軟體架構師推薦適當使用 Mixins 和組合作為替代方案,以保持代碼簡潔。
多型(Polymorphism)支持接口實作和運算符重載,實現多型操作。透過抽象類別和介面來實現多型 UI 元素處理方式一致化。使用 Provider、Riverpod 等狀態管理工具提升多型在狀態管理上的應用效率和靈活性。 社群專家指出,多型在 Flutter 中能顯著提升元件的重複利用率,有助於構建更具模組化的 UI 結構。
代碼可讀性與維護性提供清晰語法結構,使代碼易於閱讀和理解嚴格界定各層次間責任劃分,提高維護效率推動 Clean Architecture 在 Flutter 專案中的普及資深開發者認為,遵循最佳實踐能顯著降低技術債務

封裝(Encapsulation)是將資料和操作這些資料的方法捆綁在一個單元內,稱為類(class)。它將物件的內部狀態隱藏起來,僅透過定義良好的介面暴露必要的功能。在 Dart 中,封裝是透過存取修飾符如 public、private 和 protected 來實現的。預設情況下,成員是公開的,但你也可以使用底線 (_) 將它們標記為私有。例如:

class BankAccount {   double _balance = 0; // Private property    void deposit(double amount) {     _balance += amount;   }    void withdraw(double amount) {     if (_balance >= amount) {       _balance -= amount;     } else {       print('Insufficient funds.');     }   }    double getBalance() {     return _balance;   } }  void main() {   var account = BankAccount();   account.deposit(1000);   print('Current balance: ${account.getBalance()}'); // Output: Current balance: 1000   account.withdraw(500);   print('Remaining balance: ${account.getBalance()}'); // Output: Remaining balance: 500 }

繼承是一種機制,允許一個類別(子類別)從另一個類別(父類別)繼承屬性和方法。這促進了程式碼的重用,並在類別之間建立了一種階層關係。範例:

class Animal {   void speak() {     print('Animal speaks.');   } }  class Dog extends Animal {   @override   void speak() {     print('Dog barks.');   } }  void main() {   var dog = Dog();   dog.speak(); // Output: Dog barks. }

多型(Polymorphism)允許不同類別的物件被視為共同超類別的物件。這使得程式碼具有靈活性和可擴充套件性。例如:

class Shape {   void draw() {     print('Drawing a shape.');   } }  class Circle extends Shape {   @override   void draw() {     print('Drawing a circle.');   } }  class Rectangle extends Shape {   @override   void draw() {     print('Drawing a rectangle.');   } }  void main() {   Shape circle = Circle();   Shape rectangle = Rectangle();    circle.draw(); // Output: Drawing a circle.   rectangle.draw(); // Output: Drawing a rectangle. }

抽象化(Abstraction)是一種隱藏複雜實現細節,僅顯示物件基本特徵的過程。它有助於降低程式設計的複雜度,並有效管理大型程式碼庫。

例如:

abstract class Animal {   void speak(); }  class Dog extends Animal {   @override   void speak() {     print('Dog barks.');   } }  void main() {   var dog = Dog();   dog.speak(); // Output: Dog barks. }

方法覆寫(Method overriding)發生在子類別提供了一個已經在其父類別中定義的方法的具體實現時。這允許子類別根據其特殊需求來調整繼承方法的行為。

範例:

class Animal {   void speak() {     print('Animal speaks.');   } }  class Dog extends Animal {   @override   void speak() {     print('Dog barks.');   } }  void main() {   var dog = Dog();   dog.speak(); // Output: Dog barks. }

方法過載(Method overloading)是指能夠定義多個具有相同名稱但引數不同的方法。Dart 語言並不直接支援方法過載,但你可以透過使用可選引數或命名引數來達成類似的功能。例如:

class Calculator {   int add(int a, int b) {     return a + b;   }    double add(double a, double b) {     return a + b;   } }  void main() {   var calc = Calculator();   print(calc.add(2, 3)); // Output: 5   print(calc.add(2.5, 3.5)); // Output: 6.0 }

繼承是一種機制,透過這種機制可以從現有的類(超類)建立一個新的類(子類),並繼承其屬性和方法。這促進了程式碼的重用,並在類之間建立起層次關係。單一繼承是指當一個類從單一的超類中繼承屬性和方法時發生的情況。在 Dart 語言中,一個類只能擴充套件自一個超類。

範例:

class Animal {   void eat() {     print('Animal is eating.');   } }  class Dog extends Animal {   void bark() {     print('Dog is barking.');   } }  void main() {   var dog = Dog();   dog.eat();  // Output: Animal is eating.   dog.bark(); // Output: Dog is barking. }

在這個例子中,Dog 類別繼承了 Animal 類別中的 eat() 方法。多層繼承涉及到一個繼承鏈,其中一個子類別成為另一個類別的超級父類別。這種結構建立了一種具有多層繼承關係的階層關係。例如:

class Animal {   void eat() {     print('Animal is eating.');   } }  class Dog extends Animal {   void bark() {     print('Dog is barking.');   } }  class Labrador extends Dog {   void swim() {     print('Labrador is swimming.');   } }  void main() {   var labrador = Labrador();   labrador.eat();  // Output: Animal is eating.   labrador.bark(); // Output: Dog is barking.   labrador.swim(); // Output: Labrador is swimming. }

在這裡,拉布拉多犬類別繼承自狗類別,而狗類別又繼承自動物類別。階層式繼承涉及多個子類別從單一超級類別繼承。每個子類別共享來自超級類別的共同特徵,但可能具有其自身的專門行為。

範例:

class Animal {   void eat() {     print('Animal is eating.');   } }  class Dog extends Animal {   void bark() {     print('Dog is barking.');   } }  class Cat extends Animal {   void meow() {     print('Cat is meowing.');   } }  void main() {   var dog = Dog();   dog.eat();  // Output: Animal is eating.   dog.bark(); // Output: Dog is barking.    var cat = Cat();   cat.eat();  // Output: Animal is eating.   cat.meow(); // Output: Cat is meowing. }

在這個例子中,Dog 和 Cat 類別都繼承自 Animal 類別,共享其 eat() 方法。


封裝(Encapsulation)是物件導向程式設計(Object-Oriented Programming,簡稱 OOP)的核心原則之一,旨在將資料(變數)和操作資料的方法(函式)繫結成一個單位,即類別(class)。這種方法限制了對某些物件元件的直接訪問,對於防止未預期的幹擾和濫用至關重要。在 Dart 和 Flutter 中,封裝主要透過三種方式實現:成員變數封裝、函式封裝和類別封裝。讓我們透過實際例子詳細探討每種型別。



成員變數封裝涉及透過將變數設為私有並透過公共方法提供受控訪問來保護物件的內部狀態。這確保了物件的資料只能以定義好的方式進行修改。例如:

class Person {   String _name; // Private variable    Person(this._name);    // Getter for _name   String get name => _name;    // Setter for _name   set name(String name) {     if (name.isNotEmpty) {       _name = name;     }   } }  void main() {   var person = Person('John');   print(person.name); // Output: John   person.name = 'Doe';   print(person.name); // Output: Doe }

在這個例子中,變數 _name 是私有的,只能透過 getter 和 setter 方法進行存取或修改。這種封裝措施確保所有對 _name 的修改都經過驗證。方法封裝則涉及將某些方法設為私有,以控制物件資料的內部操作方式。僅公開必要的方法,使內部實現保持隱藏。

範例:

class Calculator {   // Public method   int add(int a, int b) {     return _performAddition(a, b);   }    // Private helper method   int _performAddition(int a, int b) {     return a + b;   } }  void main() {   var calc = Calculator();   print(calc.add(2, 3)); // Output: 5 }

這裡,_performAddition 方法是私有的,只能在 Calculator 類內部存取。公開的 add 方法則暴露了這一功能,同時隱藏了內部運作機制。類封裝涉及將相關的變數和方法捆綁到單個類中。這促進了模組化和可重用性,使程式碼更加有條理且易於管理。

例子:

class Car {   String _model;   int _year;    Car(this._model, this._year);    // Public method to display car info   void displayInfo() {     print('Model: $_model, Year: $_year');   } }  void main() {   var myCar = Car('Tesla', 2021);   myCar.displayInfo(); // Output: Model: Tesla, Year: 2021 }

在這個範例中,Car 類別封裝了 _model 和 _year 變數以及 displayInfo 方法。這種組織方式確保所有與汽車相關的資訊和行為都被歸類在一起。

在物件導向程式設計(OOP)中,抽象是一個關鍵原則,它透過建模適合問題的類來簡化複雜系統。抽象有助於隱藏實現細節,只向使用者展示功能。在 Dart 和 Flutter 中,可以以多種形式應用抽象來建立乾淨、可維護和模組化的程式碼。本部落格將涵蓋 OOP 中不同型別的抽象:資料抽象、過程抽象、使用公開規範進行的抽象以及使用私有分類器進行的抽象。讓我們深入探討吧!

資料抽象專注於僅暴露必要的特徵,隱藏不必要的細節。在 Dart 中,資料抽象通常透過使用抽象類別和介面來實現。範例:

abstract class Animal {   void sound(); // Abstract method }  class Dog extends Animal {   @override   void sound() {     print('Bark');   } }  void main() {   Animal myDog = Dog();   myDog.sound(); // Output: Bark }

在這個例子中,`Animal` 類別定義了一個抽象方法 `sound()`,該方法由 `Dog` 類別實現。`sound()` 的實作細節對於使用 `Animal` 類別的使用者來說是隱藏的,只暴露了必要的功能。

過程抽象涉及將複雜的過程或操作抽象成更簡單、更易管理的方法。這有助於將複雜的功能拆解成較小、可重複使用的方法。

範例:

class MathOperations {   double calculateArea(double radius) {     return _calculateCircleArea(radius);   }    // Private method for detailed calculation   double _calculateCircleArea(double radius) {     return 3.14 * radius * radius;   } }  void main() {   var math = MathOperations();   print(math.calculateArea(5)); // Output: 78.5 }


在這裡,MathOperations 類將計算圓面積的過程抽象到 _calculateCircleArea 私有方法中。使用者可以透過 calculateArea 方法與其互動,而不需要了解底層的計算細節。在 Dart 中的公共指定符用於暴露類的一部分,同時隱藏其他部分。這對於定義清晰的類介面並保護其內部狀態至關重要。

例子:


class BankAccount {   String _accountNumber;   double _balance;    BankAccount(this._accountNumber, this._balance);    // Public method to get account balance   double get balance => _balance;    // Public method to deposit money   void deposit(double amount) {     if (amount > 0) {       _balance += amount;     }   } }  void main() {   var account = BankAccount('123456789', 1000.0);   account.deposit(500);   print(account.balance); // Output: 1500.0 }

在這個例子中,_accountNumber 和 _balance 變數是私有的,但 balance 的 getter 方法和 deposit 方法是公開的。這樣設計可以控制對餘額的訪問,同時保持帳號資訊的隱藏。在 Dart 中,私有分類器用於將細節封裝在類內,使得某些屬性和方法無法從類外部訪問。範例:

class Employee {   String _name;   double _salary;    Employee(this._name, this._salary);    // Public method to get employee details   String getDetails() {     return 'Name: $_name, Salary: $_salary';   }    // Private method to calculate bonus   double _calculateBonus() {     return _salary * 0.1;   }    // Public method to get bonus   double get bonus => _calculateBonus(); }  void main() {   var employee = Employee('Alice', 50000);   print(employee.getDetails()); // Output: Name: Alice, Salary: 50000   print(employee.bonus); // Output: 5000.0 }

物件導向程式設計:掌握封裝、繼承、多型和抽象的關鍵


在這個例子中,_calculateBonus 方法是私有的,只能在 Employee 類別內部存取。bonus 取值器公開了計算出的獎金,同時隱藏了實際的計算過程。

理解物件導向程式設計(OOP)的四大支柱——封裝、繼承、多型和抽象——對於撰寫乾淨、可維護且可擴充套件的 Dart 和 Flutter 應用程式至關重要。掌握方法覆寫和超載等概念進一步增強了你設計和實現強健軟體解決方案的能力。透過精通這些概念,你可以充分利用 OOP 的力量來構建複雜且可擴充套件的應用程式。

理解 Dart 和 Flutter 中不同型別的繼承對於設計高效且易於維護的程式碼庫也至關重要。透過有效地利用繼承,可以促進程式碼重用、建立階層關係並輕鬆構建可擴充套件的應用程式。無論你是在建立簡單的類別階層或是設計複雜的物件結構,繼承在塑造你的 Dart 和 Flutter 專案架構中扮演著至關重要的角色。


歡迎在下方留言分享你對於 Dart 和 Flutter 中物件導向程式設計(OOP)的想法和經驗!

參考來源

物件導向程式設計- 維基百科,自由的百科全書

物件導向程式設計(英語:Object-oriented programming,縮寫:OOP)是種具有物件概念的程式設計典範,同時也是一種程式開發的抽象方針。它可能包含資料、特性、程式碼 ...

來源: 维基百科

物件導向的三大特性: 封裝,繼承,多型

物件導向的三大特性: 封裝,繼承,多型物件導向的三大特性:封裝、繼承、多型A.封裝性(Encapsulation): 封裝(Encapsulation)的概念就是在程式碼 ...

來源: HackMD

Python —物件導向程式設計(Object-Oriented Programming, OOP)基礎 ...

物件導向的三大重點為封裝(Encapsulation)、繼承(Inheritance)、多型(Polymorphism),透過這三種方法,可使在寫程式時將內容模擬成現實的情況,換言之 ...

來源: Medium

菜雞與物件導向(3): 封裝

封裝(Encapsulation) 的概念很直覺:將物件視作一個整體,實作內容隱藏起來,讓使用者只需要知道怎麼使用即可。例如說,當你去販賣機買飲料, ...

封裝(物件導向程式設計) - 維基百科,自由的百科全書

在物件導向程式設計方法中,封裝(英語:Encapsulation)是指,一種將抽象性函式介面的實作細節部份包裝、隱藏起來的方法。同時,它也是一種防止外界呼叫端,去存取 ...

來源: 维基百科

物件導向程式設計四大支柱之三與四:抽象與多型 - 數聚點

物件導向程式設計四大支柱 · 封裝(Encapsulation); · 繼承(Inheritance); · 抽象(Abstraction); · 多型(Polymorphism)。

來源: Substack

物件導向程式設計- Visual Basic

Visual Basic 為包括封裝、繼承和多型在內的物件導向程式設計提供完整支援。 「封裝」指的是將一組相關的屬性、方法和其他成員,視為單一單位或物件。

來源: Microsoft Learn

Object Oriented物件導向-3:封裝(Encapsulation)、繼承(Inheritance) ...

大話設計模式:每個物件都包含它進行操作所需要的所有資訊,這個特性稱為封裝,因此物件不必依賴其他物件來完成自己的操作。 封裝的優點; 一、良好的 ...

來源: GitHub

MS

專家

相關討論

❖ 相關專欄