PHP5 的個體導向能力問題 - magic methods 和 interface
我這兩天和 racklin 討論 PHP 和 SPL 的內容。經過這兩天的討論,我覺得我們愈來愈了解現在 PHP 語言的特性與未來發展方向的議題了。
我們的討論重點圍繞在 PHP5 的 magic method 與 interface 兩方面的內容。
如果 PHP 沒有 magic method,那麼就要提供一個 interface 叫 ObjectAccess
,宣告兩個方法 __set, __get 。
反過來,如果 PHP 不用 interface ArrayAccess
,而用 magic method ,那麼有一組 magic method: offsetSet, offsetGet。
interface 是個空殼,類別實際上就是要有對應的 member method。
再回到 racklin 的例子:
這樣就限定 $obj 必須是一個集合型態的實例了。但第7行出現了一個很好笑的矛盾:原生的陣列竟然不被接受!? 對 C++ 使用者而言,這種情形表示程序員的功力不夠,竟然忘了覆載型別轉換運算子。
此一矛盾起因於 PHP 沒有把 primitive type 個體化,又沒有把 primitive type 的方法自動繫結到個體方法上。若 primitive array 個體化了,亦即 primitive array 是 Array 類的實例,也必然實作了 ArrayAccess interface ,第7行就不會發生錯誤。
再者,在 PHP 中不常這麼做,更常見的情形是引數可以是任何型態。這種實作方式牽涉到 PHP5 不像 C++/Java 具有同名異式的覆載能力。理由可見 參數寫作風格。對靜態語言而言,使用函數簽名按理來說是好的。但 PHP 目前不支援同名異式的覆載。這表示我如果想限定型別,就要增加許多不必要的方法名稱。例如: abcByArray(), abcByArrayAccess()。於是我們一般寫成:
上例中也顯示 PHP 未平滑地將 primitive type 的方法(函數)與個體導向機制繫結,故 is_array()
只能判斷 primitive array ,而不能判別是否為一具有 ArrayAccess 行為的個體。
對compiler而言,interface幫助它在編譯程式碼的過程中檢查個體是否擁有所需的方法。但對 PHP 而言(它好歹算動態語言),任何個體都有可能在執行後增刪方法。而且從最後一個例子可以看出,實際上我們還是被迫只關心 $obj 有沒有這個方法。注意, PHP 仍要求類別必須宣告它實作了 ArrayAccess ,PHP 才會將 [ ]
運算子繫結到 offsetGet
方法。
Ruby 的做法是,我只關注有沒有我需要的方法。使用者也可以隨時抽換他的方法內容。如下例:
正如 racklin 所說,每種語言都有自己的特性。不過從我上述的例子來看, PHP 似乎得了雙重人格症,而且兩邊的角色都沒有高水準的演出。
當然,基於我個人主觀意識,並不希望看到這種情形繼續下去。
樂多舊回應