[心得] 淺聊物件導向在 PHP 中的觀念和應用 # 2

看板PHP作者 (銀色)時間13年前 (2011/07/30 21:15), 編輯推噓6(600)
留言6則, 6人參與, 最新討論串1/1
聊過了類別的封裝,我們要繼續實例討論物件導向的第二特性, 也就是所謂擴充的能力。 這有點像是在玩遊戲, 假設你在遊戲裡操作著一隻小豬,這隻小豬有一個特技叫做「吃」, 每次當你遇到某個謎題時,都必需讓小豬「吃」的能力成長, 可能需要吃鐵,吃火,吃土…等等, 而每當你拿到一種新的「吃」的能力,小豬的封號也會隨之改變。 上面這個情景很類似我們對於物件導向擴充的描述, 讓我們繼續延伸前一篇的程式內容,把這個遊戲情景給套用進去 目前我們手上持有的是一個叫 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

07/30 22:01, , 1F
推!
07/30 22:01, 1F

07/30 22:44, , 2F
07/30 22:44, 2F

07/30 22:57, , 3F
推 :P
07/30 22:57, 3F

07/31 10:12, , 4F
推!
07/31 10:12, 4F

07/31 18:40, , 5F
NICE
07/31 18:40, 5F

10/02 01:47, , 6F
推!
10/02 01:47, 6F
文章代碼(AID): #1ED0Fyzj (PHP)