物件導向程式設計(OOP)中的「類別」與「介面」是兩大核心支柱,但初學者常混淆兩者的使用時機。本文透過一個輕鬆幽默的「家庭分工」情境——定義「老婆」的角色與行為,來對比「繼承基類」與「實作介面」兩種設計模式的程式碼結構與思維差異。
此模式將共通邏輯(如煮飯流程:檢查食材→缺料買菜→烹飪)封裝在基類 Wife 中,子類 I 繼承並複寫特定方法。
<?php
/**
* 基類:定義預設家務流程
*/
class Wife {
public function Cook($howToCook, $vegetableArray) {
$this->BuyVegetables($vegetableArray);
foreach ($howToCook as $dish) {
// 簡化邏輯:檢查食材是否備齊
if (!in_array($dish['one'], $vegetableArray)) {
$this->BuyVegetables([$dish['one']]);
}
if (!in_array($dish['two'], $vegetableArray)) {
$this->BuyVegetables([$dish['two']]);
}
echo "烹飪:{$dish['operation']}{$dish['one']}{$dish['two']}<br>";
}
}
public function BuyVegetables($vegetableArray) {
echo "去菜市場採購:" . implode('、', $vegetableArray) . "<br>";
}
public function WashClothes() {
echo "1. 干洗外套<br>2. 洗衣機洗褲子<br>3. 手洗襪子<br>";
}
public function DoHouseholdDuties() {
echo "1. 掃地<br>2. 拖地<br>3. 擦桌子<br>";
}
}
/**
* 子類:繼承 Wife,複寫或擴充行為
*/
class I extends Wife {
public function PlayGames() { echo "打遊戲<br>"; }
public function PlayBasketball() { echo "打籃球<br>"; }
public function WatchTV() { echo "看電視<br>"; }
// 複寫煮飯方法:定義專屬菜單並呼叫父類流程
public function Cook() {
$menu = [
['one' => '豬肉', 'two' => '芹菜', 'operation' => '炒'],
['one' => '土豆', 'two' => '牛肉', 'operation' => '燒'],
];
$stock = ['豬肉', '雞蛋', '酸奶', '香菇', '芹菜', '土豆', '牛肉'];
parent::Cook($menu, $stock);
}
public function WashClothes() { parent::WashClothes(); }
public function DoHouseholdDuties() { parent::DoHouseholdDuties(); }
}
// 使用範例
$me = new I();
$me->Cook();
$me->PlayGames();
?>
特點:I 自動擁有 Wife 的所有預設實作,適合「是一種」關係且需共享大量預設邏輯的場景。但耦合度較高,子類受限於父類結構。
介面 Wife 僅定義「必須做什麼」(方法簽章),不提供任何實作。類別 I 實作介面,必須自行撰寫所有方法邏輯。
<?php
/**
* 介面:定義契約,不含實作
*/
interface Wife {
public function Cook($howToCook, $vegetableArray);
public function BuyVegetables($vegetableArray);
public function WashClothes();
public function DoHouseholdDuties();
}
/**
* 實作類別:完全自訂邏輯
*/
class I implements Wife {
public function PlayGames() { echo "打遊戲<br>"; }
public function PlayBasketball() { echo "打籃球<br>"; }
public function WatchTV() { echo "看電視<br>"; }
public function Cook($howToCook, $vegetableArray) {
$this->BuyVegetables($vegetableArray);
foreach ($howToCook as $dish) {
if (!in_array($dish['one'], $vegetableArray)) {
$this->BuyVegetables([$dish['one']]);
}
if (!in_array($dish['two'], $vegetableArray)) {
$this->BuyVegetables([$dish['two']]);
}
echo "烹飪:{$dish['operation']}{$dish['one']}{$dish['two']}<br>";
}
}
public function BuyVegetables($vegetableArray) {
echo "去菜市場採購:" . implode('、', $vegetableArray) . "<br>";
}
public function WashClothes() {
echo "1. 干洗外套<br>2. 洗衣機洗褲子<br>3. 手洗襪子<br>";
}
public function DoHouseholdDuties() {
echo "1. 掃地<br>2. 拖地<br>3. 擦桌子<br>";
}
}
// 使用範例(介面型別宣告,利於多型)
$me = new I();
$me->Cook([
['one' => '豬肉', 'two' => '芹菜', 'operation' => '炒'],
['one' => '土豆', 'two' => '牛肉', 'operation' => '燒'],
], ['豬肉', '雞蛋', '酸奶', '香菇', '芹菜', '土豆', '牛肉']);
?>
特點:I 擁有完全自主權,可依需求調整演算法細節。介面可被多個類別實作(如 Husband, Robot),支援「依賴倒置」與單元測試 Mock,架構彈性極高。
| 比較維度 | 抽象基類繼承 | 介面實作 |
|---|---|---|
| 關係語意 | Is-a (是一種) | Can-do (能做某事) |
| 程式碼重用 | 高(繼承父類邏輯) | 低(需自行實作) |
| 耦合程度 | 緊密(父類變動影響子類) | 鬆散(僅綁定契約) |
| 多型支援 | 單一繼承鏈 | 可實作多個介面 |
| 適用場景 | 核心邏輯穩定、共享度高 | 需多樣化實作、強調解耦 |
PaymentGatewayInterface),實作類別專注業務邏輯。AbstractBase 供繼承,減少重複代碼。I 需要「會煮飯」與「會洗衣」兩種能力,考慮注入 CookingService、LaundryService 物件,而非硬性繼承單一基類。透過「家庭分工」這個生活化隱喻,我們清楚看到:繼承是為了重用「實作」,介面是為了定義「契約」。實務開發中,靈活運用兩者,配合組合模式,才能打造出高內聚、低耦合、易維護的 PHP 應用架構。