0

我创建了一个库来解析不同类型的数据。这就是图书馆的使用方式。

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

$data是一个实例NamedDataCollection。这是类定义

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定义)。如果我将其设为静态,则无法使用这些方法。

注意:示例代码代表我遵循的模式。但是类名和场景只是示例。为示例提供现成的解决方案并不能解决我的实际问题。

4

2 回答 2

2

当您编写自己不想实例化(加载定义)所有解析器类只是为了找出要采用哪个解析器类时,您需要更改您的设计。

首先,我强烈建议你让你的解析器只做解析——而不是解析和感知数据。一件物品,一份工作。把事情简单化。

数据类型嗅探(说明使用哪个解析器)实际上是在工厂内进行的。对于工厂方法来说,这可能有点多。即使它需要决定实例化哪个具体的类名,也不意味着它需要为此封装整个逻辑。此外,这很可能会随着时间的推移而改变,因为您将随着时间的推移添加解析器。

因此下一个改进可能是有一个工厂方法对象而不是工厂方法。也就是说,您实例化工厂,它允许您随着时间的推移用新的工厂类型替换它。

你应该能够一步一步地做到这一点。

  1. 从接口中删除can_parse方法。如果解析器想在内部使用它,请将其设为私有。但它不属于任何具体的接口(作为抽象强制执行)Parser
  2. 制作DataParser和实际ParserFactory对象。实例化它并将它传递到需要它的地方。factory将方法重命名为,makeParser以便更清楚地知道发生了什么。
  3. 将该工厂对象转换为接口并将类重命名为实现该接口的具体名称。

ParserFactory::makeParser随着时间的推移,您现在可以根据需要重新实现该方法。如果需要,只需创建一个新的工厂对象。如果这种情况经常发生变化,您可能也希望将决定注入工厂,但我会推迟该步骤。您现在所做的可能只是最好的方法,我首先只会将其移出解析器。例如,针对潜在类列表进行测试,并捕获任何ParseExceptions表明解析器无法(成功)解析数据的信号。保持您的软件正常工作,只需将它们彼此分开一点即可。之后,您可以创建第二个以不同方式做事的工厂对象,您需要做的就是更改对象,应用程序的其余部分(阅读:所有现有解析器)可以保留并且因此不需要任何更改。

这应该会给您留下您需要进行试验的所有自由,这是决定如何实例化(制作)具体解析器的最佳方式。

于 2013-03-03T10:58:39.700 回答
0

一般来说,它看起来是正确的。工厂根据输入生成对象。我不确定 can_parse()。我认为应该由工厂做出决定,而不是将其委托给特定对象。

看看这里http://sourcemaking.com/design_patterns/factory_method

于 2013-03-02T22:18:09.503 回答