終於到了介紹物件的章節了~
這張的內容會稍微有點多,所以我拆分成上、下兩個部分來介紹!
上半部分會介紹
- 基本介紹
- 自動載入
- 建構元/解構元
- 繼承
- 範疇解析運算符號
- static 和 final 關鍵字
- 抽象類別
- 介面
- 匿名類別
1. 基本介紹
一個類別最主要由以下3種元素組成
- 屬性、方法
- 常數
- 存取權限
底下分別做更詳細的介紹
1. 屬性、方法
存在於類別裡面的變數就稱作屬性,也可以叫做成員變數
而存在於類別裡面的函式就稱為方法
不過宣告變數的方式稍微有些不太一樣
在宣告變數的前面必需要加上修飾關鍵字
像是 public、protected 或是 private (這部分請看下方存取權限的介紹~)
另外還有 var
使用 var 的效果和 public 相同,不過在 PHP 5.3 的版本後被棄用了,所以並不建議使用 var 來宣告變數!!!
2. 常數
在類別當中也可以宣告常數
不過僅限定於使用 const 這個關鍵字來宣告~
而宣告常數的時候,可以忽略存取權限的修飾詞,效果會和使用 public 相同~
不過一般還是建議加上存取權限修飾詞比較好~
3. 存取權限
存取權限有分為 3 種
- public
表示公開的權限,可以直接從外部取得或是呼叫
這也同時代表著一個類別和外部溝通的平台
因為類別具有封裝性的原則,一般建議減少 public 的存取權限
僅僅保持最低限度的對外開放即可
而使用 public 宣告的變數或方法也會被繼承到子類別中~
關於繼承的概念在後續會有更詳細的介紹
- private
表示私有的權限,無法被外部存取也不能被繼承
是完全屬於該類別的
考量到類別的封裝性原則,一般建議多採用 private 進行宣告
不過當然還是要考量到其他應用方面~
但是若僅僅是要用來進行自己類別內的運用時
就會建議使用 private 而非 public~
- protected
表示受保護的權限,簡單來說就是結合了安全性以及可被繼承的特點!
當使用 protected 宣告時,被宣告的變數或方法不可以被外部存取,但是可以被繼承
雖然看起來結合了很多優點,不過在使用上一般還是會是會斟酌使用!!!
總結:
有些不該被繼承的私有變數都還是會建議以 private 宣告
然後要給外部呼叫存取的則以 public 做宣告
當方法僅提供給子類別使用的方法才以 protected 宣告~
一般除了這些存取權限以外,還會考量到子類別的方法存取權限不可以小於父類別的權限…等問題 (好饒舌啊~~)
來進行更詳細的存取權限宣告~
2. 自動載入
在前面的章節曾經提到過
可以使用 include 或是 require 關鍵字來引入外部的 PHP 檔案,將每個 PHP 檔案進行分工~
如此一來就可以讓單一個 PHP 檔案看起來更加簡潔
在後續的維護方面也會更加容易
而 PHP 另外有提供自動載入的函式 spl_autoload_register (適用於 PHP 5.1.0 以上和 PHP 7 的版本)
其餘的 PHP 版本則使用 __autoload 方法,因為已經被棄用了,所以這裡就不再提到了~
使用自動載入的好處就是
我們不需要使用一大堆的 include 和 require~
PHP 會自動幫我們載入在檔案裡有使用到的外部資源檔案
讓撰寫上更加方便!
1 | spl_autoload_register(function($class){ |
因此僅需要在 spl_autoload_register 函式裡面撰寫引入外部 PHP 檔案的程式碼即可~
3. 建構元/解構元
這部分要介紹的是有關於物件被建立以及終結的時候會觸發的方法~
- 建構元:當物件被建立時,會觸發建構元內的程式碼
- 解構元:當物件被終結時,會觸發解構元內的程式碼
若不需要改寫建構元/解構元的情況下,可以在類別中省略~
1. 建構元
在新版的 PHP 中,__construct 函式代表一個類別的建構元
(舊版則是使用和類別相同的名稱去代表建構元)
簡單來說建構元就是在一個物件剛被建立時觸發
不過和其他程式語言不同的地方在於!!!
當子類別建立時,並不會觸發父類別的建構元!!!
需要另外呼叫 parent::__construct() 才會呼叫父類別的建構元~
並不會先建立父類別再建立子類別喔
這是 PHP 和其他物件導向語言最大的不同之處~
1 | class BaseClass |
2. 解構元
在 PHP 當中是使用 destruct 函式去呼叫解構元
和建構元相反的是解構元,是當一個物件被終結的情況下會呼叫解構元
若有需要呼叫父類別的解構元的話
也需要另外呼叫 parent::destruct() 才可以喔!
1 | class BaseClass |
4. 繼承
延續前面提到的繼承
這一章節就要來詳細介紹啦~
簡單來說就是屬於 public 和 protected 存取權限的方法以及變數都可以由某個類別繼承給另外一個類別
而繼承的子類別可以直接取用繼承類別定義的方法和變數~
如此一來就可以簡化類別的程式碼,也可以讓彼此的分層更加的明確~
在 PHP 當中是透過 extends 關鍵字去繼承另一個類別
就可以從繼承的父類別那邊將屬於 public 和 protected 權限的方法、變數都繼承過來
而在子類別這邊也可以透過重新定義相同名稱的方法或變數來達到覆寫的效果
特別要注意的是!!!
改寫後的存取權限必須要大於原本定義的存取權限才可以~
例如:
原先在 A 類別使用 protected 去定義某個方法
當 B 類別去繼承 A 類別並且想要改寫 用 protected 定義的某個方法時,存取權限只可以是 protected 或是 public 才行~
另外在覆寫的時候,傳入的參數和原先定義的參數名稱不相同也可以正常運行喔~
1 | class BaseClass |
5. 範疇解析運算符號
在 PHP 裡面的範疇解析運算符號是使用雙冒號 (::)
在雙冒號的後方可以接上變數或方法的名稱
它代表的意思是指定方法或變數的使用範圍
簡單來說就是我要呼叫或取用某個範圍定義的某個變數或方法
比較常見的有 3 種,分別如下:
- self::
- static::
- parent::
前面 2 種我會在後續有獨立的小章節來介紹,所以這邊僅先針對第 3 種來做介紹
其實在前面的範例就已經有看過第 3 種的寫法出現了~
它的意思就是我想要去取用/呼叫繼承父類別的某個變數或方法
下面來個小範例,透過 parent:: 方式取用父類別裡面定義的常數再列印出來
1 | class BaseClass |
比較特別的是
在類別當中定義的常數是屬於靜態的類型,因此是可以在不實做類別的情況下直接取用的
所以當上面的範例改寫成下方的程式碼時,會得到相同的結果~
1 | class BaseClass |
而關於靜態的這個概念則在下一章節會介紹~
6. static 和 final 關鍵字
這一章節要介紹 static 和 final 關鍵字~
1. static
這個部分剛好延續前面提到的靜態的概念
我將靜態歸納成以下 3 種特性:
- 定義成靜態的方法或變數屬於類別本身,表示所有類別只有 1 個
- 延續特性 1,被定義成靜態的方法不可以被實做的類別取用,僅可以透過範疇解析運算符號的方式來取用
- 將特性 2 換句話說,不需要實做類別也可以直接取用靜態的方法或變數
只需要在變數或方法前面加上 static 關鍵字,就可以將變數或方法定義成靜態的屬性
1 | class BaseClass |
針對類別或方法的不同用途,可以將部分方法定義成靜態類型
可以讓整體程式碼上更為簡潔~
2. final
而 final 的意思顧名思義就是最後的
當某個類別或方法不希望被覆寫的時候,就可以使用 final 關鍵字~
而當使用 final 關鍵字去定義某個類別或方法時,會有以下的特性:
- 被定義 final 的類別不可以被繼承
- 被定義 final 的方法不可以被覆寫
- final 關鍵字不可以使用在變數上,替代是使用 const 去定義常數
再來就來看看下面的範例吧~
1 | final class BaseClass |
要定義 final 的方法的話也是在方法前面加上 final 關鍵字
因此這裡就不多作示範啦~~~
7. 抽象類別
在我們撰寫許多類別的時候,常常會發現會出現許多相同的方法,僅在變數的細微的地方不同而以
這時候我們就可以使用到抽象類別!!! 幫我們省略掉許多重複的程式碼~
在一般的應用情景上
我們可以將重複的程式碼分離成一個方法,並將各自類別不同的程式碼撰寫成許多個抽象方法
讓繼承的類別去實做~
說到這裡就先來提一下抽象類別的幾個特性吧~
- 當類別當中的某個方法被定義成抽象方法時,這個類別就必須被定義成抽象類別
- 抽象類別不可以被實做,只可以被繼承
- 抽象方法也不可以被實做內容,只需要定義存取權限、名稱以及參數即可
- 繼承的子類別必需要實做所有的抽象方法
- 繼承類別實做抽象方法的存取權限只可以是相同或更高的存取權限
看完之後你可能會想說,我難道不能直接繼承一個普通的類別就好嗎~
當然是可以的!
只不過抽象類別因為特性的關係,更具有強制性,可以確保每個繼承的子類別都具有各自的方法可以被呼叫
而在概念上,抽象類別也就像是一個共通的種類一般
比如說有一個貓和狗的類別,我可以建立一個動物的抽象類別
然後建立一個叫聲的方法,讓貓和狗類別分別去定義各自的叫聲~
在 PHP 當中,只需要在類別或是方法的前面加上 abstract 關鍵字就可以建立抽象類別或方法了~
1 | abstract class Animal |
透過實做抽象方法,可以實做屬於自己類別的方法內容
在呼叫相同方法時,去執行不同的內容~
8. 介面
在這一部分要介紹的是介面
介面和抽象類別其實非常相似
在定義介面的時候,不需要把方法實做,而繼承的子類別必需要實做所有的方法
在使用上,我們可以從概念來區分兩者的差異
抽象類別比較像是共通的種類,例如男生女生都是人,就可以建立一個人的抽象類別
而介面就比較像是一個規範,比如說生活的規範,裡面可能包含吃東西、穿衣服…等等 (有點爛的比喻…如果有想到更好的話我再更新吧…)
以這種概念來區分的話,可以幫助我們更加明確的了解該使用抽象類別亦或是介面
當然在實做上都是可以互通的~
不過使用介面的好處就在於說
只要這個類別有實做某個介面,我就可以確定它有對應的方法可以呼叫
不用擔心會呼叫到一個不存在的方法或變數
從上面的說明來看,可以更加理解介面像是一個規範的概念~~
另外一點則是
一個類別只可以繼承一個父類別
所以當我們想要同時“繼承”多個的時候,就可以以介面的方式去幫助我們達成期望的結果~
再來一樣要提到介面的幾個特性:
- 介面裡面定義的所有方法都必須為 public 的存取權限
- 實做的子類別必需要實做所有在介面裡定義的方法
- 當子類別同時實做多個介面時,不同介面之間不可以有相同名稱的方法
- 當子類別在實做介面時,方法必需要和介面裡定義的完全一致
- 介面可以使用 extends 關鍵字去繼承其它介面
看完特性會發現其實介面和抽象類別是真的非常相似,也常常會讓人搞混
我的建議是從概念著手,可以讓我們更加釐清兩者的區別~
在 PHP 當中,則是使用 interface 這個關鍵字去定義一個介面
而類別則是使用 implements 關鍵字去實做介面
再來就看看下面的範例吧!
1 | interface BaseInterface{ |
可以透過介面和抽象類別去完成更加簡潔的程式碼~
9. 匿名類別
上半章的最後一個小章節要介紹的是匿名類別
顧名思義就是可以不需要提供一個特定的名稱
一般我們要實做一個類別的時候,會像下面的程式碼1
$obj = new Class();
而匿名類別則是省略了前面的特定名稱,例如1
testFunc(new Class());
一般會使用匿名類別的情況有兩種原因
- 簡單目的: 也就是說並不會在其他地方使用到,因此可以省略給定一個特定名稱 (當然還是會依據程式碼而定)
- 單行物件的寫法: 直接在要傳遞物件的地方,將類別以及內容一併實做後傳入,例如下方的程式碼範例:
1
2
3
4
5$util -> setLogger(new Class{
public function log($msg){
echo $msg;
}
});
其實上述的兩種原因都蠻類似的
適當的使用可以幫助讓程式碼更加簡潔
不過我個人比較不建議使用單行物件的寫法~
將類別另外定義會比較容易維護,在程式碼的方面也不會太過於混亂~
總體來說,還是會根據用途和程式碼撰寫的方式去決定該使用哪種方式~
如果後續不會需要繼續使用同一物件的話,不妨嘗試使用看看匿名類別的方式~