具有对象初始化的工厂类 - 试图避免静态

时间:2011-03-30 13:24:55

标签: php static factory-pattern

我试图为我们的系统设计一组工厂类,工厂创建的一些对象也需要在正确使用之前进行初始化。

示例:

$foobar = new Foobar();
$foobar->init( $qux, ... );
// $foobar ready for usage

对于相同的示例,假设$qux对象是Foobar所需的唯一依赖项。我想要达到的目的是:

$foobar = Foo_Factory( 'bar' );

为了避免需要在整个系统中传递$qux对象并将其作为另一个参数传递给工厂类,我想直接执行Foobar的初始化在工厂类:

class Foo_Factory {

    public static function getFoo( $type ) {

        // some processing here
        $foo_name = 'Foo' . $type;
        $foo = new $foo_name();
        $foo->init( $qux );

        return $foo;
    }

}

我想到的解决方案很少,但没有一个是理想的解决方案:

  1. 为工厂类添加$qux的静态setter方法,并让它在私有静态变量中存储对$qux的引用。系统可以在开始时设置$qux,工厂类可以防止将来的任何更改(出于安全原因)。
    虽然这种方法有效,但使用静态参数存储对$qux的引用在单元测试期间存在问题(例如,由于其静态状态,它可以在各个测试之间幸存下来)。
  2. 使用Singleton模式创建一个新的上下文类,让工厂类使用它来获取对$qux的引用。这可能比选项#1更简洁一些(尽管我们将静态问题从工厂类移到上下文类中。)
  3. 一直使用依赖注入,即将$qux传递给使用工厂类的任何对象,并让该对象将其作为另一个参数传递给工厂类:Foo_Factory::getFoo($type, $qux);
  4. 与上面(#3)相同,但不是沿着系统传递$qux,而是传递工厂类的实例(即在这种情况下,它不是静态的,而是可实例化的)。
  5. 你会推荐什么?上面提到的四种替代品中的任何一种,还是有更好的方法来做到这一点?

    注意:我不想在这里进入static is evil火焰战争,只是想找到最佳解决方案。

2 个答案:

答案 0 :(得分:5)

我会一直使用依赖注入。但是,不要在任何地方传递$ qux,只需在Dependency Injector Container中注册它,然后让容器对其进行排序。在Symfony Component发言:

// Create DI container
$container = new sfServiceContainerBuilder();

// Register Qux
$container->setService('qux', $qux);
// Or, to have the DI instanciate it
// $container->register('qux', 'QuxClass');

// Register Foobar
$container->register('foobar', 'Foobar')
          ->addArgument(new sfServiceReference('qux'));

// Alternative method, using the current init($qux) method
// Look! No factory required!
$container->register('altFoobar', 'Foobar')
          ->addMethodCall('init', array(new sfServiceReference('qux')));

答案 1 :(得分:4)

我只是将Factory方法设置为非静态方法,并将其传递给需要该工厂的每个对象。

要设置工厂,您需要在构造函数中使用$qux参数提供它。

class Foo_Factory {

    public function __construct($qux) {
        $this->qux = $qux;
    }

    public function getFoo( $type ) {
        // some processing here
        $foo_name = 'Foo' . $type;
        $foo = new $foo_name();
        $foo->init( $this->qux );

        return $foo;
    }
}

使用这种方法,您应该可以在需要与工厂一起工作的类中轻松使用,而不会出现传递服务容器或注册表的“麻烦”。

对于这个例子,我会使用直接的方法来传递你真正需要的对象,而不是将它抽象到Container类中。

我认为应该为整个项目做出决定是使用DIC还是注册表还是普通的旧版DI。我非常喜欢DIC而不是注册表,但更喜欢普通的DI。对于给定的上下文,很难争论或反对某种方法。

总结我的观点:如果静态工厂是问题,那就让它非静态。

希望我理解你的帖子;)