依赖注入和/或工厂模式

3

我不确定我的标题是否正确,因为我甚至不确定我是否使用了正确的术语。

我有一个包含属性的类,该属性是一个对象。在设置此属性时,必须创建该对象。我的问题是如何在没有紧密耦合的情况下完成这个过程?

示例:

class A
{
    protected $_depending;

    protected $_somePropertyObject;

    public function __construct(\Lib\Dependency $depending)
    {
        $this->_setDepending($depending);
    }

    protected function _setDepending(\Lib\Dependency $depending)
    {
        $this->_depending = $depending;
    }

    public function setSomeProperty($someProperty)
    {
        // I want to prevent this
        $this->_somePropertyObject = new \Lib\some\Object($someProperty);
    }
}

我可以通过构造函数传递所需的对象,但如果需要更多怎么办?
如果我正确理解工厂模式,那么会发生什么变化?我仍然需要在某个地方创建对象吗?不是对象本身,而是工厂。再次紧耦合?对我来说似乎是无限的。重构类时,它们的制作方式和位置被隔离出来。
如果我将setSomeProperty函数设置为仅接受\Lib\some\Object,则仍然需要由传递它的父对象创建。似乎只是将其创建位置转移了一下?
希望我表达得足够清楚。
提前感谢您!
编辑:我的问题是创建何时、在哪里、为什么创建的顺序。
2个回答

6
在依赖注入模式中,工厂的目的是为另一个实例生产实例,而不需要该实例知道如何生产它。
在其核心,"工厂"只是一个对象返回器:当调用时返回一个实例。
这在更强大的语言中更容易看到。例如,在Python中,类是可调用的(没有new运算符),调用类会产生该类的实例。因此,如果类不需要参数,则类可以成为自己的工厂。同样,任何返回实例的零参数函数都可以被认为是一个工厂。这使得依赖注入非常清晰且无需样板文件。
在更严格的语言(Java/C++/C#静态类型传统,或者在PHP中类或函数不完全是一等公民的情况下),你需要将依赖注入隐藏在一个"模式"后面,因为"设计模式"缺少语言特性。在PHP 5.3+中,您可以使用闭包作为工厂,或者您可以按照Java/C#方式定义一个FactoryInterface和每个工厂的新类。
例如,对于您的类,您可以这样做:
class Aprime extends A
{
    public function setSomeProperty($somePropertyFactory)
    {
        $this->_somePropertyObject = $somePropertyFactory();
    }
}

在这个类中,setSomeProperty 需要一个零参数可调用的“工厂”,您可以像这样创建它:
$other_dep_factory = function(){ return new SomeOtherClass(); };

或者像这样:
class ClassFactory {
    function __construct($classname, $args=array()) {
        $this->class = new ReflectionClass($classname);
        $this->args = $args;
    }

    function __invoke() {
        return $this->class->newInstanceArgs($this->args);
    }
}

$other_dep_factory = new ClassFactory('SomeOtherClass');

在PHP 5.3之前,您需要像Java一样执行此操作:

interface IObjectFactory {
    function getObject();
}

// this B-and-D interface is optional
// it has no body because PHP doesn't support
// type-hinting return values
interface ISomeOtherClassFactory {}


class SomeOtherClassFactory implements ISomeOtherClassFactory {
    function getObject() {
        return new SomeOtherClass();
    }
}

class Aprime extends A
{
    public function setSomeProperty(ISomeOtherClassFactory $somePropertyFactory)
    {
        $this->_somePropertyObject = $somePropertyFactory->getObject();
    }
}


$other_dep_factory = new SomeOtherClassFactory();
$myAprimeObject->setSomeProperty($other_dep_factory);

那么什么时候使用工厂模式?每当一个对象需要创建另一个对象时。如果该对象只需要使用另一个对象,只需传入一个实例即可。


但是我应该在哪里创建这个工厂对象呢?我应该将它作为依赖项传递吗?最好不要使用静态方法。 - John
在创建需要它的东西之前,先创建它。 - Francis Avila
2
在类的外部创建它并将其传递,就像我上面展示的示例代码一样。每个程序都有一个地方,你可以在那里创建东西而不是定义东西。(例如,一个main()方法或某个框架调用的方法)你可以在其上构建更多声明性抽象,但最终你必须在某个地方说new Something() - Francis Avila
我应该为每个类创建一个工厂吗? 工厂将能够创建对象可能需要的所有类。 例如:类A获取FactoryA来创建所有对象类A需要的对象。 在FactoryA中,它直接拥有每个类的子工厂? 例如,FactoryA可以创建配置类并且可以是数据库连接,文件等。 - John
我认为您在描述“注册表”或“工厂工厂” - 一种返回其他类型对象/工厂的对象。更好的方法是,类不要直接使用这样的东西,因为它们与您的注册表接口相关联。您正在过度复杂化事情。准则是,如果您的类包含new ClassName表达式,请传递一个工厂实例,然后将其转换为“$factoryObject()”。我编写的“ClassFactory”类应该涵盖90%的用例。闭包覆盖了100%! - Francis Avila
显示剩余2条评论

0

当需要收集“信息”来创建存储在$_somePropertyObject中的对象时,我喜欢使用工厂模式。例如,假设您必须分配值以实例化它或在实例化后运行一些方法。

此外,您还需要考虑是否可能需要稍后更改继承树。如果您现在可能将$_somePropertyObject分配给\Lib\some\Object,则可能希望稍后能够轻松地将其替换为\Lib\some\FancyObject。如果您使用依赖注入,则可以轻松地交换子类型。

这里是一个入门指南:http://net.tutsplus.com/tutorials/php/the-whens-and-whys-for-php-design-patterns/

另外,这也是一个好资源:https://dev59.com/BXI95IYBdhLWcg3w8iz1#2083455


你制造的工厂在哪里,如何创建、定义和收集信息?从第一个被称为的类开始。 - John
看到您的编辑。我仍然在想在哪里创建工厂对象本身? - John

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接