首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >内部工厂与外部工厂

内部工厂与外部工厂
EN

Stack Overflow用户
提问于 2011-04-15 04:37:36
回答 1查看 614关注 0票数 5

我正在考虑用PHP实现工厂模式的两种不同方法中的一种。我不知道这些变体是否有合适的名称,所以现在我将它们称为内部工厂和外部工厂。

内部工厂:工厂方法在类本身中作为静态公共方法实现

代码语言:javascript
复制
<?php
class Foo
{
    protected
        $loadedProps = false;

    public static factory ($id)
    {
        $class = get_called_class ();
        $item = new $class ($id);
        if ($item -> loadedProps ())
        {
            return ($item);
        }
    }

    public function loadedProps ()
    {
        return ($this -> loadedProps);
    }

    protected function loadPropsFromDB ($id)
    {
        // Some SQL logic goes here
    }

    protected function __construct ($id)
    {
        $this -> loadedProps = $this -> loadPropsFromDB ($id);
    }
}
?>

外部工厂:工厂及其初始化的项作为单独的实体实现

代码语言:javascript
复制
<?php

class Foo
{
    protected
        $loadedProps = false;

    public function loadedProps ()
    {
        return ($this -> loadedProps);
    }

    protected function loadPropsFromDB ($id)
    {
        // Some SQL logic goes here
    }

    public function __construct ($id)
    {
        $this -> loadedProps = $this -> loadPropsFromDB ($id);
    }
}

abstract class FooFactory 
{
    public static factory ($id)
    {
        $item = new Foo ($id);
        if ($item -> loadedProps ())
        {
            return ($item);
        }
    }
}
?>

现在在我看来,每一种方法都有其优点。

前者允许您对外部世界隐藏构造函数。这意味着创建Foo对象的唯一方法是通过工厂。如果项的状态不能从DB加载,那么工厂将返回NULL,您可以在代码中轻松地检查它。

代码语言:javascript
复制
if ($item = Foo::factory ($id))
{
    // ...
}
else
{
    // The item failed to load.  Handle error here
}

工厂还可以创建Foo的任何子类的对象,而无需任何修改。

然而,它似乎有一些缺点。首先,类必须实现工厂,这可能会将真正属于其他地方的职责放在类中。内部工厂版本肯定比外部工厂版本产生更大的类。

至于外部工厂,它更干净,因为工厂本身不在类中,我也不必担心一个类承担了更多的责任。外部工厂也可能更适合依赖注入。

然而,它也有自己的一系列缺点。首先,要在工厂中构建的项的构造函数必须是公共的,因为PHP没有包的概念,也没有类成员的“包”保护级别。这意味着没有什么可以阻止程序员只做新的Foo ()并绕过工厂(尽管这可能会使单元测试更容易)。

另一个问题是FooFactory只能创建Foo对象,而不能创建它的任何子类。这可以通过向FooFactory添加另一个参数来指定类名来解决,但随后工厂必须进行内部检查,以确定指定的对象类实际上是Foo的后代。

因此,基本上,这两种方法的相对优点是什么,您会推荐哪种方法?

此外,如果它们有比内部或外部工厂更合适的名称,我想知道它们。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-04-15 06:03:43

实际上,工厂是已建立的创造性设计模式,因此您可以在GoF书籍或Sourcemaking:上了解它们的用途

  • Abstract工厂池-创建多个classes
  • Builder系列的实例-将对象构造与其representation
  • Factory方法分离-创建多个派生classes
  • Object池的实例-通过回收不再存在于use
  • Prototype中的对象来避免昂贵的资源获取和释放-要复制或克隆的完全初始化的实例

在这些模式中有一些重叠,特别是在Factory Method、Abstract Factory和Builder之间,当您不是创建对象系列,而只是创建一种类型的对象时,区别就更模糊了。所以,为了简单起见,让我们假设内部和外部工厂是正确的术语。

就我个人而言,我总是喜欢外部工厂而不是内部工厂,因为你已经给出了原因:我可以使用Dependency Injection和can separate the responsibilities。由于static methods are death to testability和由于它们引入的耦合而可以是considered harmful的,所以我将使工厂成为一个真实的对象,并使用非静态方法。

你提到的两个缺点根本不是真正的缺点。

我想不出一个原因,为什么我想要prevent a developer来实例化工厂创建的对象。实际上,当使用Unit-Testing时,我希望自己创建该对象,并用Mocks and Stubs替换所有依赖项。I also dont believe that developers should be babysitted too much。考虑到PHP的脚本性质,将ctor从私有更改为公共太容易了,无论如何都无法有效地防止。

至于工厂无法创建其他类的另一个问题,这不是真的。Factory的想法实际上是创建各种类型的对象族。甚至可以显式地允许Factory方法创建子类。无论你是使用开关/case还是在工厂中使用各种方法来实现它,都取决于你自己。也没有理由不将工厂与Builder相结合,或者让工厂的工厂反过来封装逻辑来创建对象。从而消除了对您提到的任何内部检查的需要(也可以通过类型提示来满足)。

工厂的另一个可行的替代方案是使用像Symfony Components DIC这样的Dependency Injection Container,并且主要通过该容器来管理对象。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5669363

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档