2008年03月27日

延續《分割程式功能以及 mix-in 和 include》的討論

Tags: php mix_in

延續《分割程式功能以及 mix-in 和 include》的討論。tokimeki 說: 既然已經在外部定義了function,直接調用function不就好了嗎?

我直接委派函數的原因是 PHP 語法限制。用函數比用靜態成員函數(類別方法)或一般成員函數(個體行為)要容易寫。請看我在另一篇的回應: 這牽涉到 PHP 的動態能力限制。


至於不直接調用 function 這點,那是因為直接調用 function 的方式將損失動態性,或者說 "彈性"。如下例:

以這例子來說,我只需要知道我要調用 say 行為,其他都不用管了。如果直接調用函數,哪要如何寫?把 allSay($a); 這行改成沒有彈性的三行?

tokimeki又說: 我所需要的是「擴充」該類別的功能,換言之,他必須能像繼承一般,可以調用類別內protected的方法

先看繼承,我的 MixableClass 的設計目標就是要能繼承,這點並沒有什麼問題。至於說到擴充 protected method 的事,Ruby 可以做到這一點,但 PHP 不行。以 PHP 的動態能力來看,不允許你混入 protected 或 private method。

Ruby 可以 'open class' 後改變原有類別的任何方法 (包括 protected method)。如下例:

第20行的 a.run 可以證明上例並不是定義一個新的 A 類別覆蓋先前的 A 類別。因為第2段 class A 的定義中,並沒有提到 run 這個行為。如果是覆蓋而非 'open' ,那麼就不會有 run 行為。

想學 Runy 這麼做嗎? PHP 會告訴你發生重覆定義類別的錯誤。

或許我們可以在 MixableClass 的 prototypeDelegate() 中多加一個指定存取屬性的參數,然後在 __call() 中排除嘗試調用註明 protected 的混入方法。不過 PHP 的動態能力不足以應付這種情形: 我們不知道是誰 call? 別忘了個體調用自己的行為時,也會透過 __call() (當它原本沒有定義時)。

按 PHP 目前的能力來看,想要達成你的需求,要嘛用 include ,要不就用 hook 的技巧:你預先在方法定義中埋入 hook points ,然後你在外部插入 hooks 。


Posted by shirock at 樂多Roodo! │13:51 │回應(5)引用(0)PHP
樂多分類:學術/學習 工具:加入樂多書籤編輯本文
Ads by Roodo! 

引用URL

http://cgi.blog.roodo.com/trackback/5763473
回應文章
我試著整理一下我的論點:
1. 我的目的是為了分割程式的功能,也就是說,原本調用的方式是$x->y();,分割完後還是用$x->y();。

2. 我並不是為了想取得更多的動態性,反而我個人是認為過多的動態可能會引起混亂,這也就是為何我只用__call而不用__set的原因。

3. 每個插件對於他的宿主的非私有方法擁有直接調用的能力,反過來也是成立的。唯一的例外是有同名方法情況時,會根據本身物件具有的方法為優先,這點在以宿主物件調用方法時,保障了插件不會覆寫宿主原有的方法,這件事對於插件本身的地位來說是至關重要的。
Posted by tokimeki at 2008年03月29日 00:43
另外我覺得,不論我們設計的機制或是想達到的目的為何,既然都不能免除使用__call加上函數表來達到我們要的目的,那麼效率的低落是在所難免。
令人欣慰的事情是,這樣的作法可以讓我們在「個人」可行的範圍內,減少我們維護程式碼的負荷。

不過最後我還是要文人相輕一下:我不喜歡用eval,也不喜歡用反映,我個人是覺得那個真的算是奇技淫巧,未來PHP改版後能不能適用,其實我不太有把握之後的相容性。
Posted by tokimeki at 2008年03月29日 00:48
本來這個系列應該到此結束,但最近的實做跟重構過程,我發現幾個問題,想跟你請教:
1. 用這類方式擴充類別的能力,有時會碰到相依性的問題,當然我可以複寫建構式來先行載入相關的「插件」,可是當某個插件被使用的機率很高時,我是否該改寫他,將他重構進入「宿主」類別中呢?
2. 有時我需要某些橫切的機制,比如說紀錄、快取等等,但有時候這些機制會跟我選擇的儲存體相關(檔案、資料庫),這些又整個糾結在一起(比如我把紀錄放在資料庫,那如果資料庫存取發生錯誤怎麼辦),有沒有一個比較清晰的方式可以解決?
Posted by tokimeki at 2008年04月15日 00:50
1.如果是 Ruby/JavaScript/Python 等等動態語言開發的專案,那是完全不需要考慮 "重構進入宿主" 這種事的。因為不需要這麼做。

你在先前的回應中提到了"函數表",事實上,基於個體導向的動態語言本身就是一個巨大的函數表管理器。所謂的建立類別與方法等動作,就是重覆的插入、置換函數表。因為它的內部機制就是如此,我們是沒有必要考慮 "重構進入宿主" 這種事的。

但對於 PHP 而言,因為它在這方面帶有 Java 的影子(說實話,這真是個錯誤決策),所以你可能需要這麼做。我會建議你先做效能測試,如果 "載入機制" 會造成瓶頸,那就把它寫進去。

2.我的習慣是,當我調用一個動作失敗時,就回傳 false 或丟出例外訊息,交給 Container 去處理。我看不懂你想要尋求什麼?
Posted by 遊手好閒的石頭成 at 2008年04月21日 14:19

感謝你的建議,我會量測看看兩種作法的時間差異
Posted by tokimeki at 2008年04月24日 09:32