正确使用Factory方法/模式和动态加载

时间:2013-03-02 19:58:39

标签: php oop design-patterns

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

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

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

2 个答案:

答案 0 :(得分:2)

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

首先,我强烈建议您让解析器进行解析 - 而不是解析和检测数据。一个对象,一个是工作。保持简单。

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

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

您应该能够一步一步地完成这项工作。

  1. 从接口中删除can_parse方法。如果解析器希望在内部使用它,请将其设为私有。但它不属于任何具体Parser的接口(强制为抽象)。
  2. 制作DataParser和实际ParserFactory对象。实例化它并将其传递到需要的地方。将factory方法重命名为makeParser,以便更清楚发生了什么。
  3. 将该工厂对象转换为接口,并将该类重命名为实现该接口的具体名称。
  4. 您现在可以根据需要重新实施ParserFactory::makeParser方法。如果需要,只需创建一个新的工厂对象。如果这种情况经常发生变化,您可能希望将决定注入工厂,但我会推迟这一步骤。你现在做的可能只是最好的方法,我首先只会把它从解析器中移出来。例如。测试潜在类列表并捕获任何表示解析器无法(成功)解析数据的ParseExceptions。保持软件正常运行,只需将它们彼此分开。之后,您可以创建第二个以不同方式执行操作的工厂对象,您需要做的就是更改对象,应用程序的其余部分(读取:所有现有解析器)可以保留,因此不需要进行任何更改。 / p>

    这应该为您提供实验所需的所有自由,这是决定如何实例化(制作)具体解析器的最佳方式。

答案 1 :(得分:0)

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

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