最近更新: 2008-03-25

關於分割程式功能以及 mix-in 和 include

最近碰到一個分割程式功能的問題,不得已必須用到反映的功能,寫了一個簡單的抽象插件類別 http://blog.pixnet.net/HACGIS/post/15612808。 不知道你有沒有更好的解法? tokimeki

關於這個問題,我是用所謂「混成(mix-in)」的概念實踐。先前我寫過2篇: PHP 實踐 mix-in 概念PHP 實踐 mix-in 概念 part 2

另外一位網友是用 include ,參考: mix-in?。嚴格來說那不是 mix-n ,稍後說明。這有3個缺點。

第一,因為 include 會進行檔案開啟動作,當你大量使用這技巧時,將會降低效能。

第二,一個小小的語法缺點,程式外觀不甚雅觀。例如:

class A {
  var $name = 'world';
  function say() {
    include 'plugin/hello.php';
  }

  include 'plugin/otherMethods.php';
}

--
// plugin/hello.php

echo "Hello, $this->name";

--
// plugin/otherMethods.php

public function bye() {
  echo "Goodbye, $this->name";
}
--

當然,這不算什麼大缺點。但從動態性來說,卻可以一眼看出問題。稍後說明。

第三,也是最重要的一點:嚴格來說它不具動態性,不算混成(mix-in)。

混成(mix-in)的特點是「由外而內的重構」。你要混入新方法的程式碼寫在目標類別定義之外。但用 include 時,你要把它 (也就是 'include xxx' 的程式碼) 寫在目標類別定義中。而且不能在運行時變更。

使用混成(mix-in)添加新的行為時,我並不需要回頭修改目標類別的程式碼內容。而是在目標類別以外的地方做這件事。如下例,使用我之前實作的 MixableClass

A.php
class A extends MixableClass {
  var $name = 'world';
}
others.php
require_once 'A.php';
function bye() {
  echo "Goodbye, $this->name";
}

A::delegate('A', 'bye', 'bye');

$a = new A;
$a->bye();

透過混成,我不用回頭修改 A.php 這個源碼的內容,而是在別的地方做這件事。是「由外而內」的重構,所以說是 "mix-in"。

樂多舊網址: http://blog.roodo.com/rocksaying/archives/5751881.html

樂多舊回應
HACGIS@gmail.com(tokimeki) (#comment-16075849)
Tue, 25 Mar 2008 21:04:42 +0800
你的mix-in可以把外部的function加入類別內,可問題是,既然已經在外部定義了function,直接調用function不就好了嗎?

我所需要的是「擴充」該類別的功能,換言之,他必須能像繼承一般,可以調用類別內protected的方法(存取屬性的問題不考慮)。

在分割程式功能時,我是把一些不常用的方法移出核心類別,以插件的方式寫在另外一個類別中,藉由我設計的那個「插件」類別把核心與插件類別的非private方法用__call這個魔術方法掛入。

由於我不想手動寫函數表,所以利用反映的方式自動產生該函數表。

我知道那個算是奇技淫巧,不過我實在沒有比較好的方式來作~
HACGIS@gmail.com(tokimeki) (#comment-16075969)
Tue, 25 Mar 2008 21:23:19 +0800
我在補充一點,我給出的範例,核心類別是「測試」,裡面有一個protected的「測試01」方法,我在插件類別「測試:test02」中的「測試02」方法中有呼叫$this->測試01()。

這樣子為何不會出現錯誤呢?這是因為那個「_插件」類別搞的鬼,他把「測試:test02」和「測試」類別的方法公開給這兩個類別來使用。
未留名 (#comment-16088357)
Thu, 27 Mar 2008 13:56:01 +0800
回應見:

延續討論
lp81sam@hotmail.com(lp81sam) (#comment-18115019)
Mon, 08 Dec 2008 19:45:36 +0800
===============================================================

http://lp81sam.spaces.live.com/blog/cns!FE9AB5CD84013FC2!324.entry

------------------------------------------------------------------

...
function __call($name, $args)
{
$file = dirname(__FILE__) . '/module_' . $this.moduleName . '/' . $name . '.php';
if(file_exists($file)) {
return include($file);
}
}
...
------------------------------------------------------------------

------------------------------------------------------------------
現在才看到這篇文章 :p

我覺得要達到「由外而內」的重構效果
只要在__call動手腳就好拉
透過檔案去Mix-in
我寫那篇的時候就這麼想
只是沒說明,我只是在展現原理
其實我沒要跟你爭論這樣的作法是不是Mix-in
因為目前php應該本來就沒有這樣的機制
當然有你說的runkit
我只是要利用php既有的特性
去達到你要的效果
當然作法我覺得是越簡單越好
因為這些問題都是可以從底層就處理的
所以現階段的功能到哪,我就善用它
原則就是"簡單易用powerful少寫code"
當然這樣的代價
就是機器去跑
否則怎麼有哪麼多的動態語言
我想未來應該寫程式應該會變成
用說的
或是用比的
或是用眼睛瞄的吧
這樣才會符合人類的習慣 :-)

===============================================================
lp81sam@hotmail.com(lp81sam) (#comment-18115047)
Mon, 08 Dec 2008 19:53:49 +0800
歹勢
關於上面的回應
因為有部份的程式碼被截掉了
預覽的時候是沒有問題的
所以我把回應貼在這

http://lp81sam.spaces.live.com/blog/cns!FE9AB5CD84013FC2!452.entry

PS:
因為我滿懶的 :p
所以直接就複製貼上
然後也沒有加一些特殊的標籤
未留名 (#comment-18427417)
Fri, 23 Jan 2009 15:47:55 +0800
to lp81sam:

__call() 的解除方案有些缺點。例如效能,它把 __call() 當成行為函數的 gateway ,在小系統中尚可快速回應,但在大系統就會是瓶頸。

另一方面,它是全域性的影嚮,而不能區域性。在 mix-in 的概念中,你可以針對一個類別混入新行為,也可以針對一個實例(instance)混入新行為。但用 __call() 的話,影嚮的是 global 環境,而不是區域性的單一類別或單一實例。