Table of Contents
PHP 多型是物件導向程式設計中的一個關鍵概念,允許不同類別的對象被當作同一類對象處理。這項特性促進了代碼的重用、模組化和擴展性,並在設計模式中有廣泛應用。
什麼是 PHP Polymorphism
多型的概念
多型(Polymorphism)是物件導向程式設計(Object-Oriented Programming, OOP)中的三大基石之一,這三個分別是封裝(Encapsulation)、繼承(Inheritance)和多型(Polymorphism)。
多型這個字源自希臘語,意為 “多種形態”,在程式設計的世界中,它允許我們使用一個介面或一個基礎類別來表示多種不同的實現。
多型這個詞聽起來可能很艱深,但其實它的核心觀念非常簡單。想像一下你有一把「萬能鑰匙」,這把鑰匙可以開很多種不同的鎖。
多型就是程式設計中的「萬能鑰匙」!它讓你可以用同一把鑰匙來處理很多不同類型的鎖,簡短來說,就是你可以用一個通用的方式來解決一系列相似的問題。
多型的目的
多型的主要目的是為了提供一個機制,讓一個名稱可以用於多個相關但不同類型的對象。這樣可以減少代碼的重複,並允許多個類別共享同一個介面或實現。這不僅可以提高代碼的可讀性和可維護性,也讓代碼更容易測試和重用。
使用多型有幾個明顯的優點:
代碼重用:你不必為每種特定情況寫一堆不同的代碼。一個多型的函數或方法就能搞定!
易於維護:如果你有一個功能要應用在五個不同的類別上,而這個功能以後可能還會改變。使用多型,你只需要改動一個地方的代碼,而不是五個。
靈活性:多型讓你的程式更加靈活。如果未來你想要添加或修改某些功能,你可以輕鬆地做到這一點,而不會影響到其他部分的代碼。
降低耦合:多型也有助於降低程式中各個類別或模組之間的依賴。這使得系統更為靈活,更容易進行更改和維護。
多型的實現
PHP 是一個動態類型的語言,所以它對多型的支持非常自然。在 PHP 裡,你會發現有好幾種方式來實現多型,比如透過繼承(Inheritance)和介面(Interfaces)。但不管你選擇哪一種方式,核心觀念都是一樣的:讓不同的物件可以用同一種方式來處理。
在 PHP 中,多型主要通過以下幾種方式來實現:
方法覆寫(Method Overriding):在繼承的子類別中改寫基礎類別的方法。
介面(Interface):定義一個介面,然後讓多個類別實現這個介面。
抽象類別(Abstract Class):使用抽象類別作為基礎,然後讓多個子類別繼承這個抽象類別。
PHP Polymorphism 範例
基礎範例:動物叫聲
考慮一個簡單的例子,我們有一個 Animal
類別,其中有一個 makeSound
方法。我們也有幾個繼承自 Animal
的子類別:Dog
、Cat
。
// 基礎的 Animal 類別
class Animal {
public function makeSound() {
echo "Some generic animal sound";
}
}
// 繼承自 Animal 的 Dog 類別
class Dog extends Animal {
public function makeSound() {
echo "Woof!";
}
}
// 繼承自 Animal 的 Cat 類別
class Cat extends Animal {
public function makeSound() {
echo "Meow!";
}
}
在這個例子中,Animal
類別定義了一個 makeSound
方法,而 Dog
和 Cat
類別則分別覆寫(Override)了這個方法。
// 使用多型
function animalSound(Animal $animal) {
$animal->makeSound();
}
$dog = new Dog();
$cat = new Cat();
animalSound($dog); // 輸出:Woof!
animalSound($cat); // 輸出:Meow!
這裡的 animalSound
函數接受一個 Animal
類型的參數。由於 Dog
和 Cat
都是 Animal
的子類,所以它們都可以作為這個函數的參數。
進階範例:形狀和面積
問題描述
假設我們需要計算不同形狀(如:正方形、圓形、三角形等)的面積。這些形狀都有「面積」這一共同特性,但計算面積的方式對每一種形狀都是不同的。
基礎類別和介面
首先,我們可以定義一個 Shape
介面,其中包含一個 calculateArea
方法。這個方法將用於計算形狀的面積。
interface Shape {
public function calculateArea();
}
實現多型
接下來,我們可以建立幾個實現了 Shape
介面的類別,比如 Circle
、Square
和 Triangle
。
class Circle implements Shape {
private $radius;
public function __construct($radius) {
$this->radius = $radius;
}
public function calculateArea() {
return pi() * pow($this->radius, 2);
}
}
class Square implements Shape {
private $sideLength;
public function __construct($sideLength) {
$this->sideLength = $sideLength;
}
public function calculateArea() {
return pow($this->sideLength, 2);
}
}
class Triangle implements Shape {
private $base;
private $height;
public function __construct($base, $height) {
$this->base = $base;
$this->height = $height;
}
public function calculateArea() {
return 0.5 * $this->base * $this->height;
}
}
使用多型
有了這些類別後,我們可以創建一個函數來計算任何形狀的面積,而不需要知道它實際上是什麼形狀。
function calculateShapeArea(Shape $shape) {
return $shape->calculateArea();
}
$circle = new Circle(5);
$square = new Square(4);
$triangle = new Triangle(3, 4);
echo "Circle area: " . calculateShapeArea($circle) . "\n"; // 輸出:Circle area: 78.539816339745
echo "Square area: " . calculateShapeArea($square) . "\n"; // 輸出:Square area: 16
echo "Triangle area: " . calculateShapeArea($triangle); // 輸出:Triangle area: 6
在這個例子中,calculateShapeArea
函數接受一個實現了 Shape
介面的對象作為參數。由於 Circle
、Square
和 Triangle
都實現了這個介面,它們都可以作為這個函數的參數。
這種方式增加了代碼的可擴展性和可維護性。假如未來我們要加入更多種形狀,只需要讓新的形狀類別實現 Shape
介面,而不需要修改 calculateShapeArea
函數。
實用範例:資料庫操作
問題描述
在大型應用程式中,我們可能會遇到需要與多種資料庫互動的情況,例如 MySQL、PostgreSQL、MongoDB 等。每種資料庫的操作方法都有些許不同,但基本的 CRUD(Create、Read、Update、Delete)操作是類似的。
基礎介面
我們首先定義一個 Database
介面,其中包括基本的 CRUD 方法。
interface Database {
public function connect();
public function create($table, $data);
public function read($table, $id);
public function update($table, $data, $id);
public function delete($table, $id);
}
實現多型
接著,我們可以為不同的資料庫(如 MySQL 和 MongoDB)建立實現了 Database
介面的類別。
// MySQL 的實現
class MySQLDatabase implements Database {
public function connect() {
// MySQL 連接邏輯
}
public function create($table, $data) {
// MySQL 新增資料邏輯
}
public function read($table, $id) {
// MySQL 讀取資料邏輯
}
public function update($table, $data, $id) {
// MySQL 更新資料邏輯
}
public function delete($table, $id) {
// MySQL 刪除資料邏輯
}
}
// MongoDB 的實現
class MongoDBDatabase implements Database {
public function connect() {
// MongoDB 連接邏輯
}
public function create($table, $data) {
// MongoDB 新增資料邏輯
}
public function read($table, $id) {
// MongoDB 讀取資料邏輯
}
public function update($table, $data, $id) {
// MongoDB 更新資料邏輯
}
public function delete($table, $id) {
// MongoDB 刪除資料邏輯
}
}
使用多型
有了這些類別後,我們可以編寫一個通用的資料操作函數,該函數可以接受任何實現了 Database
介面的對象。
function performDatabaseOperation(Database $db, $operation, $table, $data = null, $id = null) {
switch ($operation) {
case 'create':
$db->create($table, $data);
break;
case 'read':
return $db->read($table, $id);
case 'update':
$db->update($table, $data, $id);
break;
case 'delete':
$db->delete($table, $id);
break;
}
}
$mysqlDB = new MySQLDatabase();
$mongoDB = new MongoDBDatabase();
// 使用 MySQL 進行新增操作
performDatabaseOperation($mysqlDB, 'create', 'users', ['name' => 'John', 'email' => 'john@email.com']);
// 使用 MongoDB 進行讀取操作
$userData = performDatabaseOperation($mongoDB, 'read', 'users', null, 'user_id_123');
這個設計使得我們可以靈活地在不同的資料庫之間切換,而不需要對資料操作函數進行任何修改。新增支持新的資料庫也變得相對容易,只需建立一個新的類別實現 Database
介面即可。
更所參考資源
- PHP: The Right Way
- Polymorphism – explain the polymorphism concept and show you how to implement polymorphism in PHP using abstract classes or interfaces.