各位安安,這篇終於要進入書中的正題啦!以下是作者設定的故事背景⋯⋯
Joe 是一個工程師(Joe是要對決 沒事😂),他任職的公司主要是在做一個鴨子模擬器(SimUDuck)的遊戲,遊戲功能是可以讓不同種類的鴨子在同個池塘裡游泳和呱呱叫。
遊戲初始的設計是以物件導向去寫一個鴨子的類別(class),讓其他種類的鴨子去繼承。公司在去年面臨同行競爭壓力,需要推出新功能來做出市場區隔,所以主管要求Joe要寫出一個fly()
的功能讓鴨子飛行。Joe也順著之前的方式,在鴨子的類別中新增一個fly()
的方法(method)。隔天馬上接到主管來電罵他,因為有些鴨子是不能飛的,卻因為這個繼承方式讓rubber duck(橡皮鴨)在池塘上空飛來飛去。
這其實也間接代表著,Joe在修改程式碼的時候並沒有考慮到程式後續的維護問題。主管同時也告訴他,之後會陸續增加像是DecoyDuck()
(鴨子擺飾)、RedheadDuck()
(紅頭鴨)等等,更多種類的鴨子,所以他需要用一個更簡潔的方式來寫出飛行 fly()
和發出叫聲 quack()
的功能。
既然繼承行不通,Joe轉而想要利用介面(interface)去定義 fly()
, quack()
,所以程式碼大致上會變成:
<?php
interface Flyable{
public function fly();
}
interface Quackable{
public function quack();
}
class MallardDuck implements Flyable, Quackable{
funciton fly(){
//do something
}
funciton quack(){
//do something
}
}
class RubberDuck implements Quackable{
funciton quack(){
//do something
}
}
?>
這樣的做法看似有解決問題,其實依然是很難維護的程式碼,因為並不是每種鴨子都使用一樣的叫聲及飛行方式,這也代表,每生成一個鴨子的子類別,就要去修改一次它飛行與叫聲的method內容,效率跟後續維護上會有問題。
我們已經知道,在鴨子這個父類別中,會變動的因素就是鴨子在同一行為上有不同表現。為了不要造成上述問題,所以我們將透過新的Behavior
介面去表示鴨子行為,讓不同的類別去實現,
FlyBehavior.php
interface FlyBehavior{
public function fly();
}
class Flywithwings implements FlyBehavior{
public function fly(){
echo "I'm flying!!\r\n";
}
}
class FlynoWay implements FlyBehavior{
public function fly(){
echo "I cannot fly TT\r\n";
}
}
class FlyRocketPowered implements FlyBehavior{
public function fly(){
echo "I'm flying like a freaking rocket\r\n";
}
}
QuackBehavior.php
<?php
interface QuackBehavior{
public function quack();
}
class MuteQuack implements QuackBehavior{
public function quack(){
echo ".......(Silence) \n";
}
}
class Quack implements QuackBehavior{
public function quack(){
echo "QUACK \n";
}
}
class Squeak implements QuackBehavior{
public function quack(){
echo "SQUEAK \n";
}
}
?>
Duck.php
<?php
include "FlyBehavior.php";
include "QuackBehavior.php";
abstract class Duck{
public FlyBehavior $flyBehavior;
public QuackBehavior $quackBehavior;
function __construct(){}
abstract public function display();
function setFlyBehavior(FlyBehavior $flyAction){
$this -> flyBehavior = $flyAction;
}
function setQuackBehavior(QuackBehavior $quackAction){
$this -> quackBehavior = $quackAction;
}
function performFly(){
$this -> flyBehavior -> fly();
}
function performQuack(){
$this -> quackBehavior -> quack();
}
function swim(){
echo "All duck float!";
}
}
//綠頭鴨
class MallardDuck extends Duck{
function __construct(){
$this -> quackBehavior = new Quack();
$this -> flyBehavior = new FlyWithWings();
}
function display(){
echo "I'm a real Mallard duck\n";
}
}
//橡膠鴨
class RubberDuck extends Duck{
function __construct(){
$this -> quackBehavior = new Squeak();
}
function display(){
echo "I'm a rubber duck";
}
}
//裝飾用鴨子
class DecoyDuck extends Duck{
function display(){
echo "I'm a decoy duck";
}
}
?>
DuckSimulator.php
<?php
include "./Class/Duck.php";
$mallard = new MallardDuck();
$mallard -> display();
$mallard -> performFly();
$mallard -> performQuack();
$rubber = new RubberDuck();
$rubber -> display();
$rubber -> performQuack();
Output:
封裝 encapsulate:
物件導向程式設計的原則之一, 將實作和界面分開, 以便讓同界面但不同的實作物件能以一致的面貌讓外界存取
例如:
<?php
class Pamelo{
public $name;
public $color;
function __construct($n, $c){
$this -> name = $n;
$this -> color = $c;
echo $this -> name."<br>";
echo $this -> color."<br>";
}
public function BiggerSize(){
echo "Bigger than apple";
}
}
?>
多寫一個介面,改成
<?php
interface FruitSize{
public function size();
}
class Smaller implements FruitSize{
public function size(){
echo "Smaller than apple";
}
}
class Bigger implements FruitSize{
public function size(){
echo "Bigger than apple";
}
}
class Pamelo{
public $name;
public $color;
function __construct($n, $c){
$this -> name = $n;
$this -> color = $c;
echo $this -> name."<br>";
echo $this -> color."<br>";
}
public function getSize(){
$bigger = new Bigger();
$bigger -> size();
}
}
?>
Output 為:
實作 implementation:
去實際寫出interface或是抽象類別的程式碼內容。例如文中在不同子類別鴨子中都會以不同的Flybehavior實現鴨子飛行的功能,包含Flywithwings()
, FlynoWay()
, FlyRocketPowered()
的內容。
Disclaimer
因為讀的是原文版,所以難免會有翻譯詞不達意或是專有名詞上的差異,有錯誤的話歡迎在留言區一起交流!