原文地址: PHP設計模式(三):封裝
Introduction
物件導向編程中,一切都是對象,對一個對象的封裝,也成了物件導向編程中必不可少的部分。和C/C++,Java,Python等語言一樣,PHP也支援封裝。
封裝/Encapsulation
對事物的封裝是指,將事物進行抽象後,提供抽象概念的實現的具體方法。
聽起來很拗口,還是舉鯨魚的例子。
對於鯨魚來說,需要吃東西這個行為,吃本身是一個抽象的概念,因為具體到怎麼吃,是咀嚼和消化的過程,甚至如何咀嚼和消化也是不可見的。對外部而言,可見的只是吃這一個介面,如何吃、怎麼吃,是被封裝在了鯨魚的實現中。
甚至可以說,消化系統,被封裝到了鯨魚這個對象中,對外部不可見,僅僅鯨魚自己可見。
封裝方法
和別的程式設計語言一樣,PHP也只是三種封裝概念:Private,Protected,Public。
私人/Private
私人的概念是,僅僅對象內部可見,外部不可見,如:
name = "Whale"; } public function eat($food) { chew($food); digest($food); } private function chew($food) { echo "Chewing " . $food . "\n"; } private function digest($food) { echo "Digest " . $food . "\n"; }}?>
name是鯨魚的私人屬性,chew()和digest()是鯨魚的私人方法,對於其他類來說,都是不可見的。對於現實來說,我們如果只是注重吃,並沒有必要去關心鯨魚是如何去吃的。
保護/Protected
保護的概念是,僅僅是自身類和繼承類可見,這個關鍵字的用途主要是防止濫用類的派生,另外三方庫編寫的時候會用到,防止誤用。
name = "Whale"; } public function eat($food) { chew($food); digest($food); }}?>
鯨魚類可以通過繼承使用動物類的咀嚼和消化方法,但是別的繼承鯨魚類的類就不可以再使用動物類的咀嚼和消化方法了。保護更多是用於物件導向設計,而不是為了編程來實現某個需求。
公用/Public
公用的概念就是,任何類、任何事物都可以訪問,沒有任何限制,這裡不再贅述。
Getters/Setters
Getters和Setters也叫Accessors和Mutators,在Java/C#等語言中常以get()/set()方法出現。對於這兩個東西的爭議很大,考慮下面一個類:
如果不使用Getters/Setters,我們給Price類賦值和取值一般是這樣:
priceA = 1; $price->priceB = 2; $price->priceC = 3; ... echo $price->priceA; echo $price->priceB; echo $price->priceC; ...?>
但是如果使用了Getters/Setters,Price類將變成這樣:
priceA; } public function setPriceA($price) { $this->priceA = $price; } ...}?>
這時候賦值將變成這樣:
setpriceA(1); $price->setPriceB(2); $price->setPriceC(3); ... echo $price->getPriceA(); echo $price->getPriceB(); echo $price->getPriceC(); ...?>
是不是感覺需要多敲很多代碼?這也是很多程式員不願意使用get/set的原因,造成了大量的看似無用冗餘的代碼。為什麼叫看似冗餘和無用?因為Getters/Setters是編程設計方法,而不是編程實現方法。
在物件導向程式設計中,類和類之間的訪問、互動和更新應該是通過Accessors和Mutators,也就是Getters和Setters來實現。直接存取和修改破壞了類的封裝性。
為什麼採用這種設計方式?因為程式設計是對現實問題的抽象,而在編程的工程中程式員扮演的角色往往是上帝。
考慮這樣一種情境:你朋友要求你改名,決定是否改名的人是你,而不是你朋友。在你的朋友的視覺(也就是你朋友的類),他不能直接去修改你的名字。
如果你直接採用非Getters/Setters的設計方法,事實上是程式員扮演的這個上帝修改了現實規則,允許你朋友能夠隨意更改你的姓名,顯然這是不合理的。
Summary
合理的封裝對於好的程式設計是必不可少的,雖然什麼都是Public也能解決編程問題,但是這不是用程式設計解決問題的思路。