# 6. 物件導向 PHP {%hackmd QnyEFBdERZebn4iQDXNPnA %} [不錯的Cheat Sheet](https://www.codecademy.com/learn/learn-php/modules/classes-and-objects-in-php/cheatsheet) - 概覽 ![](https://i.imgur.com/SjixeNI.png) ## 01 了解PHP物件導向 ## 02 OOP(Object Oriented Programming)概念 ### 類別與物件 ### 封裝 - 黑箱 ### 繼承 - 子類別會從超類別(父類別)繼承屬性與動作 ### 多型 - **不同的類別**對於**相同動作**可以有**不同的行為** - e.g. 同是移動,但腳踏車與汽車的運動方式不同 ## 03 使用PHP建立類別、屬性與動作 ### 類別的結構 - 初始化 ```php= //1.最簡單類別定義式 class classname { } //2.建立屬性 class classname { public $attribute1; public $attribute2; } //3.建立方法 class classname { function operation1(){ } function operation2($param1, $param2){ } } ``` ### 建構式 - PHP會在建立物件時呼叫建構式,以執行初始化工作(會自動呼叫) ```php= class classname{ function __construct($param){ echo "Constructor called with parameter ".$param. "<br />"; } } ``` ### 解構式 - __destruct() - 解構式不能接收參數 ## 04 實例化類別 - new是關鍵字 ```php= $a = new Classname("First"); $b = new Classname("Second"); ``` ## 05 使用類別屬性 - 特殊指標$this,想要**設定**或**存取**類別裡的變數時:\$this->attribute (表示:**這是**我的屬性或方法!) ```php= //類別中如何設定與存取一個屬性 class classname{ public $attribute; function operation($param){ $this->attribute = $param; echo $this->attribute; } } ``` ## 06 呼叫類別動作(方法) - 括號裡,傳入參數 ```php= $a = new classname(); $a->operation1(); $a->operation2(12, "test"); //若方法會回傳,可以使用其他變數捕捉回傳值 $x = $a->operation1(); $y = $a->operation2(12,"test"); ``` ## 07 使用private與public控制存取 - 定義:用來控制**屬性**與**方法**的可見度 - public - 預設選項 - 可以在類別外部與內部存取公用項目 - private - 只能在類別裡直接被存取 - private的項目不會被繼承 - protected - 介於public與private之間 - 只能在類別裡被存取 ```php= class manners{ private $greeting = 'Hello'; public function greet($name){ echo "$this->greeting, $name"; } } //可以省略public,因為他是預設值。不過,若你要使用其他修飾符號的話,使用public可讓程式可讀性提高 ``` ## 08 編寫存取函式 - OOP優點 => 鼓勵封裝 - 使用__get, __set強制做到封裝 ```php= class classname{ private $attribute function __get($name){ return $this->$name; } function __set($name, $value){ $this->$name = $value; } } //__get()只會回傳$attribute的值 //__set()會指派一個新值給$ -------------------------------------- //它們是如何工作?若輸入以下: $a = new classname(); //使用public時,不會用到__get(), __set()屬性 $a->attribute = 5; //私下呼叫__set(),使用$name設為attribute,$value設為5 $a->attribute //私下呼叫__get(),並將參數$name設為attribute //優點:藉由單一的存取處,可以自由地修改底層的實作 ``` ## 09 使用PHP實作繼承 - 使用extends ```php= //A是父類別,B是子類別(記法:extends自父類別) class B extends A{ public $attribute2; function operation2(){ } } //如果A類別被宣告成: class A { public $attribute1; function operation1(){ } } //以下B的物件方法與屬性都是有效的 $b = new B(); $b->operation1(); $b->attribute1=10; $b->operation2(); $b->attribute2 = 10; //注意!繼承只有單方向!子類別可以繼承父類別,但父類別無法繼承子類別 ``` ### 使用private 與 protected控制繼承的可見度 - 使用private - 不被繼承 - 使用protected - 無法在類別外被看到(類別裡才可以被看到會呼叫) - 會被繼承 ```php= <?php class A{ private function operation1(){ echo "operation1 called"; } protected function operation2(){ echo "operation2 called"; } public function operation3(){ echo "operation3 called"; } } class B extends A{ function __construct(){ $this->operation1(); $this->operation2(); $this->operation3(); } } $b = new B(); ?> //$this->operation1(); 會產生嚴重錯誤(因為private無法被繼承) //檔案最後一行加入$this->operation2(),也會嚴重錯誤(因為protected在類別外被呼叫) //$this->operation3() 可以執行(因為public類別內外都可以存取) ``` ### 覆寫(Override) - 子類別**修改**與父類別屬性、方法,稱為覆寫 ```php= class A { public $attribute = 'default value'; function operation(){ echo 'Something<br />'; echo 'The value of $attribute is '.$this->attribute.'<br />'; } } //子類別想要改變$attrubte的值,或讓operation()有新功能。使用B覆寫$attribute與operation() class B{ public $attribute = 'different value'; function operation(){ echo 'Something else<br />'; echo 'The value of $attribute is '.$this->attribute.'<br />'; } } //宣吿B不會影響A --------------------------------------- //B若要呼叫父類別的原始版本怎麼辦? parent::operation() //即A::operation(),但會使用B裡面的attribute,而不是A的attribute ``` ### 使用final避免(函式)被繼承與覆寫 - 函式前加入final,無法被子類別繼承 ```php= class A { public $attribute = 'default value'; final function operation(){ echo 'Something<br />'; echo 'The value of $attribute is'.$this->attribute.'<br />'; } } //可防止B覆寫operation() ------------------------------------------------------------ //如何完全防止一個類別被繼承?(完全不能被繼承) final class A{ } ``` ### 多重繼承![](https://i.imgur.com/wZk8nfS.jpg) - PHP不支援多重繼承 - 即,每一個子類別**只能繼承一個**父類別 - PHP提供以下兩類機制來利用多重繼承的優點 - 1. 介面(interface) - 多重繼承的替代方案 ```java= interface Displayable(){ function display(); } class webPage implements Displayable{ function display() } //webPage類別可以繼承一個類別,並實作一或多個介面 ``` - 2. 特徵(traits) - 與介面的差異:特徵可包含實作,而非只是指定一個必須實作的介面 - 可以結合許多特徵 ```php= //藉由建立類別的方式來建立特徵 trait logger{ public function logmessage($message, $level='DEBUG'){ //$將$message寫至紀錄 } } //如何使用特徵? class fileStorge{ use logger; //use是使用trait的關鍵字 function store($data){ //... $this->logmessage($msg); } } ``` ## 10 進階PHP OOP功能 - Static (靜止) - Scope Resolution Operator (::)