為什麼這篇PHP 物件導向鄉民發文收入到精華區:因為在PHP 物件導向這個討論話題中,有許多相關的文章在討論,這篇最有參考價值!作者gpmm (銀色)看板PHP標題[心得] 淺聊物件導向在 PHP 中的觀念和應用 # 2時間Sa...
聊過了類別的封裝,我們要繼續實例討論物件導向的第二特性,
也就是所謂擴充的能力。
這有點像是在玩遊戲,
假設你在遊戲裡操作著一隻小豬,這隻小豬有一個特技叫做「吃」,
每次當你遇到某個謎題時,都必需讓小豬「吃」的能力成長,
可能需要吃鐵,吃火,吃土…等等,
而每當你拿到一種新的「吃」的能力,小豬的封號也會隨之改變。
上面這個情景很類似我們對於物件導向擴充的描述,
讓我們繼續延伸前一篇的程式內容,把這個遊戲情景給套用進去
目前我們手上持有的是一個叫 dataHandler 的類別,
裡面一共具有三種不同的輸出方式,分別是
output_select
output_list
output_radio
仔細想想,每次都我們輸入資料產生出一個物件時,
實際上真正會使用到的輸出行為都只是三種中的其中一種,
那換個角度,我們是否能讓這個處理資料的類別具有合適的擴充能力?
ok,沒有命題就不會有 fu,我們需要一個假設性的命題,
如果當你在執行這個案子的時候,你敏銳的觸感神經已經預計到,
客戶很有可能又額外提出六種輸出方式和三種統一驗證,
那這個時候要做什麼就很明確了,
我們需要這個類別具有輸出上的可擴充性,套用故事裡的情境,
也就是這個小豬(類別),需要可以增加不同的吃(輸出)的能力。
當然最簡單的方法就是增加類別內的 output_* 函式,
反正客戶要幾個就加給他幾個,
但也可以這樣思考,我們有沒有可能把整個擴充方式從扁平改成立體?
既然現在面對的擴充是針對「輸出」這件事,
那就來規規矩矩把輸出部份給抽象化,
於是原本的類別會變成:
class dataHandler {
var $_data = Array ();
function __construct ($data) { (省略) }
function data_remove_sexy () { (省略) }
function data_check_add_admin () { (省略) }
function output () {
// 空的
}
}
仔細看,三種不同的 output 此刻只剩下一種,
而且這個 output 函式還是個貨真價實的空蕩蕩的函式,裡面沒有半行程式碼,
為什麼呢?
因為我們要從原本「在類別內的橫式擴充」,
改成「藉由類別衍生所產生的直式擴充」,
剛剛上面這個 class dataHandler 其實只是一個初始類別,
真正可以拿來使用的會是下面這三個子類別:
class dataHandler_OutputSelect extends dataHandler {
function output () {
// 輸出 select 的程式碼
}
}
class dataHandler_OutputList extends dataHandler {
function output () {
// 輸出 list 的程式碼
}
}
class dataHandler_OutputRadio extends dataHandler {
function output () {
// 輸出 radio 的程式碼
}
}
如果你需要進行一個 select 輸出,你會這樣使用:
$data = mysql_fetch_* ($sql);
$dh = new dataHandler_OutputSelect ($data);
$dh->output ();
如果今天是一個 radio 輸出,你會這樣使用
$data = mysql_fetch_* ($sql);
$dh = new dataHandler_OutputRadio ($data);
$dh->output ();
我知道板友們一定會開始有種被騙的感覺,這跟
$dh->output_select () 和 $dh->output_radio ()
的作法有什麼不同?而且感覺還多寫了一堆 code(額外三個 class)!
請容小弟解釋一下,這兩種不同作法當中存在著很巨大的差異,
其一是這樣的程式在擴充上會更清楚明瞭,
因為往往一個類別不會只有單個函式需要做擴充,
如果同一個類別內存在著一堆相似又不同的函式,那真的是閱讀和維護上的痛苦。
其二是這樣的作法讓物件本身操作具有了一致性,
只要是 dataHandler 的子類別,
你永遠知道它具有一個關鍵的函式是 output,而且可以毫不猶豫地呼叫它,
這點非常重要,但我們會在下一篇裡面再來詳談,
因為也就是垂直擴充的物件具有了操作的一致化,第三個物件導向特性才得以成立
其三,則是一個比較難以理解的概念,那就是「決策點」的不同,
(這部份如果看不懂也沒有太大關係,因為需要有經驗才比較能體會)
如果要打個比方,「決策」的概念比較像是籃球賽裡面,
你有整整 30 秒可以出手,跟被強迫在最後 0 秒出手的不同是一樣的,
這樣講好了,
當我們在嘗試把一整個程式邏輯慢慢拆解成幾種類別時,
有的類別存在週期會很長,有的則是很短,
也就是說,
有些類別會在你程式一開始的時候就已經產生物件,並持續使用到最後,
(最常見的就是封裝成類別的資料庫操作)
有的類別則是「當下產出物件」-「當下使用」而已,
(就像是我們剛剛的資料輸出類別)
所以此時你在設計程式流程時會有兩個選擇,
從類別 new 出物件時做決策,
(像決定你要使用哪一種輸出類別,
dataHandler_OutputSelect 還是 dataHandler_OutputRadio)
或是在最後函式呼叫時做決策,
(像決定你要使用哪一種輸出函式,
dataHandler 下的 $dh->output_select 還是 $dh->output_radio)
這當中各有各的使用時機,就端看你整體程式的運作流程要怎麼設計來決定,
其中很多複雜的運用同樣也是在導入了設計模式之後才會發現的。
講到這裡,物件導向的第二特性 - 繼承也差不多講完了。
關於繼承的詳細定義和宣告方式,不熟悉的板友可以再去看一下,
這部份小弟就不贅言了。
--
頭痛中…(炸)
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 114.45.241.224