2013-03-02 141 views
0

我創建了一個庫來解析不同類型的數據。這就是圖書館的使用方式。正確使用工廠方法/模式和動態加載

$parser = DataParser::factory($text); 
$data = $parser->parse(); 
foreach($data as $name=>$datum) 
    echo "name: $name\nData: ".$datum."\n"; 

這裏$dataNamedDataCollection一個實例。這裏是類定義

interface Parsable{ 
    // @returns NamedDataCollection 
    function parse(); 
    // @returns bool. True if it can parse the data. 
    function can_parse(); 
} 

class AParser implements Parsable{ 
    public function parse(){ 
     $this->check_header(); 
     $this->check_content(); 
     $this->parse_content(); 
     ... 
    } 
    public function can_parse(){ 
     $this->check_header(); 
     $this->check_content(); 
     ... 
    } 
} 

class BParser implements Parsable{ 
    public function parse(){ 
     ... 
    } 
    public function can_parse(){ 
     ... 
    } 
} 

class ParserRegistry{ 
    // @param $dir path where all the parsers are stored 
    public function __construct($dir){ 
    } 
    // @returns all the available parsers by scanning files. 
    public function get(){ 
    } 
} 

class DataParser{ 
    public function factory($data){ 
     $instance = null; 
     $pr= new ParserRegistry("/some/path"); 
     foreach($pr->get() as $pname){ 
      $rc = new ReflectionClass($pname); 
      $instance = $rc->newInstance($data); 
      if($instance->can_parse()) 
       return $instance; 
     } 
     throw new ParserException("No parser found", 1); 
    } 
} 

它是正確的工廠方法的用法?

有一件令我煩惱的事情是can_parse方法。 似乎有很多解析器的實例是爲了測試它是否可以解析文本而創建的。有沒有更好的方法來做到這一點?我知道我可以使用靜態方法。但大多數解析器使用parse方法的某些部分(請參閱AParser定義)。如果我將其設爲靜態,則不能使用這些方法。

注意:示例代碼代表我遵循的模式。但類名和場景僅僅是一個例子。爲示例提供現成的解決方案並不能解決我真正的問題。

+0

通過注入一個策略對象來查找這些對象,可以間接地找到匹配的解析器。這並不能回答你的問題,可能只是告訴你如何在稍後移除'can_parse'依賴。 http://en.wikipedia.org/wiki/Strategy_pattern - 但是你不應該在你的解析器中引入太多的邏輯。如果他們不能解析,他們可能會拋出一個解析異常,但除此之外,他們不能說他們是否可以解析。製作一些元信息,如content-type,以便解析器解析。 – hakre 2013-03-03 09:59:23

回答

2

看看當你寫你自己,你不想實例(負載定義)的所有解析器類,只是找出採取哪一個,你需要改變你的設計。

首先我強烈建議你讓你的解析器只做解析 - 而不是解析和數據感知。一個對象,就是一個工作。把事情簡單化。

什麼實際上數據類型嗅探(說哪個分析器使用)在工廠內。這對於工廠方法可能有點多。即使它需要決定實例化哪個具體的類名,也不一定意味着它需要封裝整個邏輯。此外,這可能會隨着時間的推移而改變,因爲您將隨着時間的推移添加解析器。

因此,下一個改進可能是工廠方法對象而不是工廠方法。也就是說,你實例化工廠,允許你用新的工廠類型隨着時間的推移來替換它。

您應該可以一步一步做到這一點。

  1. 從接口中刪除can_parse方法。如果解析器想要在內部使用它,請將其設爲私有。但它不屬於任何具體的Parser的界面(強制抽象)。
  2. 使DataParser和實際ParserFactory對象。實例化它並將其傳遞到需要的地方。將factory方法重命名爲makeParser,以便更清楚發生了什麼。
  3. 將該工廠對象轉換爲接口並將該類重命名爲實現該接口的具體名稱。

您現在可以根據需要隨時重新實施ParserFactory::makeParser方法。如果您需要,只需創建一個新的工廠對象。如果這種情況經常發生變化,那麼您可能希望做出可注射到工廠的決定,但我會推遲這一步驟。你現在做什麼可能只是最好的方法,我首先將它從解析器中移出。例如。根據潛在類的列表進行測試,並捕獲任何表示解析器無法(成功)解析數據的任何ParseExceptions。保持軟件正常工作,只是把事情彼此分開。之後,您可以創建另一個工作對象,以完成不同的事情,您只需更改對象,其餘應用程序(讀取:所有現有解析器)就可以保留,因此無需更改。

這應該給你留下你所需要的所有自由來實驗哪個是決定如何實例化(製作)具體解析器的最佳方法。

0

一般來說它看起來是正確的。工廠根據輸入生成一個對象。我不確定can_parse()。我認爲這個決定應該由工廠做出,而不是委託給特定的對象。

這裏http://sourcemaking.com/design_patterns/factory_method

+0

現在有大約30個解析器。它很快就會增長到數百個。我將它委託給解析器本身,因爲'can_parse'邏輯是特定於對象的。如果這個邏輯稍後改變,我不想改變工廠方法。 – 2013-03-03 05:30:48

+1

看起來我確實是錯的。決定應該由一個子類決定,所以你的問題的答案是你做對了。沒有更多細節,很難評論表現。 – 2013-03-03 09:37:34