為什麼要使用 PHP 物件繼承
使用 PHP 物件繼承(Object Inheritance)有多種原因和好處,以下列舉其中幾個:
代碼重用
物件繼承允許你重用已經寫好的代碼。如果你有一個類別(例如「動物」),然後需要創建與它相似但有一些特殊功能的新類別(例如「哺乳動物」、「鳥類」),你可以通過繼承來實現,而不需要重新編寫相同的代碼。
維護性
使用繼承可以讓代碼更容易維護。當基礎類別(Parent Class)進行更新或修改時,所有繼承自它的子類別(Subclasses)也會自動獲得這些更改,這樣就能減少維護的工作量。
組織化
繼承有助於更好地組織和分層你的代碼。它讓你可以用更高層次的類別來表示共同的特性,然後通過子類別來表示更具體的行為和屬性。
多型
繼承是多型(一個對象能以多種形式存在)的基礎。在子類別中,你可以重寫(Override)父類別中的方法,使得子類別能有自己獨特的行為,同時還保留父類別的共同特性。
擴展性
如果你的應用程序需要新增功能或特性,使用物件繼承可以讓這個過程更為簡單。你只需創建一個新的子類別即可,而不是修改已經存在的代碼。
封裝
繼承也能促進封裝,因為它允許子類別繼承父類別的屬性和方法,而不需要知道這些屬性和方法是如何實現的。
使用物件繼承是物件導向程式設計(OOP)中一個非常重要的概念,它能提供以上所述的諸多好處。然而,也要注意不要過度使用繼承,因為這有時可能導致代碼變得過於複雜。
繼承的目的
什麼是 PHP 物件繼承
物件導向程式設計(Object-Oriented Programming, OOP)中的「繼承」(Inheritance)是一種讓新類別(子類別)可以使用已存在的類別(父類別)的屬性和方法的機制。簡單來說,繼承允許我們建立一個新類別,這個新類別會「繼承」或「承接」既有類別的特性,並可加以擴展或修改。
或者想像一下,你有一個玩具箱,裡面有各種不同的積木。有一天,你想建造一個新的機器人積木。你發現,之前已經有一個基本的機器人積木,它有手、有腳,還會走路和說話。
現在,你想讓新的機器人積木有更多功能,比如飛行。你不需要從頭開始做一個全新的機器人,你可以拿之前的機器人來加上翅膀。這樣,新的機器人就會有手、腳、走路、說話和飛行的能力!
在程式裡面,這就像是你有一個「動物」的程式碼,它會吃東西和走路。然後你想做一個「狗」的程式碼,狗除了會吃和走,還會汪汪叫。你就可以用「繼承」,讓「狗」的程式碼繼承「動物」的程式碼,這樣「狗」就自動會吃東西和走路,你只需要加上汪汪叫的部分。
所以「繼承」就是一種讓新的東西可以用舊的東西的功能,再加上新功能的方法。
實際範例
範例:動物與狗
舉例來說,假設我們有一個名為「動物」(Animal)的類別,其中有「吃」(eat)和「走」(walk)的方法。然後,我們想要建立一個新的類別名為「狗」(Dog)。狗是一種動物,所以它應該有吃和走的能力。在這種情況下,我們可以讓「狗」類別繼承「動物」類別,這樣「狗」類別就會自動擁有「吃」和「走」的方法,無需重新編寫這些方法。
在 PHP 中,我們使用 extends
關鍵字來實現繼承:
class Animal {
public function eat() {
echo "This animal eats food.";
}
public function walk() {
echo "This animal walks.";
}
}
class Dog extends Animal {
public function bark() {
echo "The dog barks.";
}
}
在這個例子中,Dog
類別繼承了 Animal
類別。所以,Dog
類別不僅有一個自己獨特的 bark()
方法,還繼承了 Animal
類別的 eat()
和 walk()
方法。所以你可以創建一個 Dog
類別的實例,並呼叫所有這些方法:
$myDog = new Dog();
$myDog->eat(); // Output: "This animal eats food."
$myDog->walk(); // Output: "This animal walks."
$myDog->bark(); // Output: "The dog barks."
範例:新口味餅乾
假設我們有一個基本的「餅乾」(Cookie)類別,這個類別有一個方法叫做「taste」(味道)。這個方法會告訴我們這個餅乾是「酥脆」的。
class Cookie {
public function taste() {
echo "這個餅乾是酥脆的!";
}
}
現在,我們想創建一個新類別叫做「ChocolateCookie」(巧克力餅乾)。我們想讓這個巧克力餅乾保有基本餅乾的「酥脆」特性,但還要加上「有巧克力味」。
為了達到這個目的,我們會用到「繼承」。我們讓「ChocolateCookie」類別繼承「Cookie」類別:
class ChocolateCookie extends Cookie {
public function chocolateTaste() {
echo "這個餅乾有巧克力味!";
}
}
在這個範例中,「ChocolateCookie」類別使用了 extends
關鍵字來繼承「Cookie」類別。所以,「ChocolateCookie」自動獲得了「Cookie」類別的 taste()
方法。
現在我們可以創建一個「ChocolateCookie」的實例,並呼叫這些方法:
$myChocolateCookie = new ChocolateCookie();
$myChocolateCookie->taste(); // Output: "這個餅乾是酥脆的!"
$myChocolateCookie->chocolateTaste(); // Output: "這個餅乾有巧克力味!"
如你所見,我們的「ChocolateCookie」不只有巧克力的味道,還繼承了基本餅乾的「酥脆」特性!這就是「繼承」如何在程式中工作的一個範例。
範例:大學課程
假設我們有一個基本的「課程」(Course)類別。這個類別有基本的屬性,如「課程名稱」和「教授名稱」,以及一個方法叫做 getDetails()
,這個方法會列出課程的基本資訊。
// 基礎的 "Course" 類別
class Course {
// 定義屬性:課程名稱和教授名稱
public $courseName;
public $professorName;
// 建構函數:初始化課程名稱和教授名稱
public function __construct($courseName, $professorName) {
$this->courseName = $courseName;
$this->professorName = $professorName;
}
// 方法:獲取課程的基本資訊
public function getDetails() {
echo "這門課是 {$this->courseName},由 {$this->professorName} 教授。";
}
}
現在,假設我們想要創建一個更具體的「程式設計課程」(ProgrammingCourse)類別。我們想要這個新類別除了有基本課程的所有資訊之外,還要有「使用的程式語言」這個新的屬性。
我們可以讓「ProgrammingCourse」類別繼承「Course」類別,並加入新的屬性:
// "ProgrammingCourse" 類別繼承自 "Course" 類別
class ProgrammingCourse extends Course {
// 新增屬性:使用的程式語言
public $languageUsed;
// 建構函數:初始化課程名稱、教授名稱,以及使用的程式語言
public function __construct($courseName, $professorName, $languageUsed) {
// 使用 parent 關鍵字呼叫父類別的建構函數
parent::__construct($courseName, $professorName);
// 初始化新屬性
$this->languageUsed = $languageUsed;
}
// 方法:獲取課程的詳細資訊,包括使用的程式語言
public function getDetails() {
// 使用 parent 關鍵字呼叫父類別的 getDetails 方法
parent::getDetails();
// 添加新的資訊
echo " 使用的程式語言是 {$this->languageUsed}。";
}
}
在這個例子中,ProgrammingCourse
使用了 extends
關鍵字來繼承 Course
類別。因此,它繼承了父類別的所有屬性和方法。此外,我們還對 getDetails()
方法進行了擴展,讓它能顯示新的「使用的程式語言」屬性。
我們可以這樣使用這個新類別:
// 創建一個 "ProgrammingCourse" 的實例
$myCourse = new ProgrammingCourse("資料結構", "張教授", "Python");
// 呼叫 getDetails 方法,顯示詳細資訊
$myCourse->getDetails(); // 輸出: "這門課是 資料結構,由 張教授 教授。 使用的程式語言是 Python。"
這範例展示了如何從一個基礎類別(Course)繼承,然後添加額外的特性(ProgrammingCourse)。
範例:汽車與電動車
這次讓我們以「車輛」(Vehicle)和「電動車」(ElectricCar)為例,來談談「方法覆寫(Override Method)」這一概念。
首先,我們建立一個基礎的「車輛」(Vehicle)類別。這個類別有一個方法叫做 fuelType()
,用來描述車輛使用的燃料類型。
// 定義一個基礎的 "Vehicle" 類別
class Vehicle {
// 方法:描述車輛使用的燃料類型
public function fuelType() {
echo "這輛車使用汽油。";
}
}
接著,我們創建一個「電動車」(ElectricCar)類別,這個類別繼承自「車輛」(Vehicle)類別。因為電動車的燃料類型和一般車輛不同,我們需要覆寫 fuelType()
方法。
// "ElectricCar" 類別繼承自 "Vehicle" 類別
class ElectricCar extends Vehicle {
// 方法覆寫:描述電動車使用的燃料類型
public function fuelType() {
echo "這輛車使用電能。";
}
}
在這個例子中,ElectricCar
類別使用了 extends
關鍵字來繼承 Vehicle
類別。然後,我們覆寫了 fuelType()
方法,使其輸出「這輛車使用電能」,以反映電動車的特性。
現在,讓我們看看如何使用這兩個類別:
// 創建一個 "Vehicle" 的實例
$myVehicle = new Vehicle();
$myVehicle->fuelType(); // 輸出: "這輛車使用汽油。"
// 創建一個 "ElectricCar" 的實例
$myElectricCar = new ElectricCar();
$myElectricCar->fuelType(); // 輸出: "這輛車使用電能。"
這個例子展示了如何透過「方法覆寫(Override Method)」來改變繼承自父類別的方法。當我們呼叫 fuelType()
方法時,ElectricCar
會使用自己覆寫後的版本,而不是使用 Vehicle
的原始版本。
範例:人和學生
這次我們用「人」(Person)和「學生」(Student)的例子來說明如何在子類別中呼叫父類別的建構子(constructor)。
首先,我們有一個「人」(Person)類別,其中有一個建構子用於設定「名字」(name)和「年齡」(age)。
// 定義一個 "Person" 類別
class Person {
public $name;
public $age;
// 建構子:設定名字和年齡
public function __construct($name, $age) {
$this->name = $name;
$this->age = $age;
}
}
接著,我們建立一個「學生」(Student)類別,這個類別繼承自「人」(Person)類別。學生除了有名字和年齡,還有「學校」(school)。
我們會在這個子類別的建構子中呼叫父類別的建構子,使用 parent::__construct()
來初始化名字和年齡。
// "Student" 類別繼承自 "Person" 類別
class Student extends Person {
public $school;
// 建構子:設定名字、年齡,以及學校
public function __construct($name, $age, $school) {
// 呼叫父類別的建構子來設定名字和年齡
parent::__construct($name, $age);
// 設定學校
$this->school = $school;
}
}
最後,我們來看如何使用這些類別:
// 創建一個 "Student" 的實例
$myStudent = new Student("小明", 20, "某大學");
// 輸出該實例的資料
echo "名字:{$myStudent->name}, 年齡:{$myStudent->age}, 學校:{$myStudent->school}";
// 輸出:名字:小明, 年齡:20, 學校:某大學
這個例子展示了如何在子類別(Student)的建構子中,透過 parent::__construct()
呼叫父類別(Person)的建構子。這樣可以確保父類別的屬性(名字和年齡)也得到適當的初始化。
這種做法讓我們可以在不修改父類別代碼的情況下,為子類別添加新的屬性(學校)和功能。
最後
通過以上範例,你已不知不覺學到了幾個物件關鍵知識。
繼承(Inheritance)
學習如何使用 extends
關鍵字來創建一個新的子類別(subclass)。
方法覆寫(Method Overriding)
了解如何在子類別中覆寫(override)來自父類別(parent class)的方法。
父類別建構子的呼叫(Parent Constructor Invocation)
學習如何使用 parent::__construct()
來呼叫父類別的建構子(constructor)。
封裝(Encapsulation)
學習如何封裝代碼,使其更安全、更容易維護。
代碼組織(Code Organization)
通過使用繼承,理解如何更有效地組織和模組化你的代碼。
物件繼承的基礎知識,對之後的多型應用至關重要,熟悉繼承之後再來學習多型就簡單啦。
更多參考文章:
- PHP Inheritance
- Inheritance – how to extend a class for code reuse.