我正在考虑用PHP实现工厂模式的两种不同方法中的一种。我不知道这些变体是否有合适的名称,所以现在我将它们称为内部工厂和外部工厂。
内部工厂:工厂方法在类本身中作为静态公共方法实现
<?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);
}
}
?>外部工厂:工厂及其初始化的项作为单独的实体实现
<?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,您可以在代码中轻松地检查它。
if ($item = Foo::factory ($id))
{
// ...
}
else
{
// The item failed to load. Handle error here
}工厂还可以创建Foo的任何子类的对象,而无需任何修改。
然而,它似乎有一些缺点。首先,类必须实现工厂,这可能会将真正属于其他地方的职责放在类中。内部工厂版本肯定比外部工厂版本产生更大的类。
至于外部工厂,它更干净,因为工厂本身不在类中,我也不必担心一个类承担了更多的责任。外部工厂也可能更适合依赖注入。
然而,它也有自己的一系列缺点。首先,要在工厂中构建的项的构造函数必须是公共的,因为PHP没有包的概念,也没有类成员的“包”保护级别。这意味着没有什么可以阻止程序员只做新的Foo ()并绕过工厂(尽管这可能会使单元测试更容易)。
另一个问题是FooFactory只能创建Foo对象,而不能创建它的任何子类。这可以通过向FooFactory添加另一个参数来指定类名来解决,但随后工厂必须进行内部检查,以确定指定的对象类实际上是Foo的后代。
因此,基本上,这两种方法的相对优点是什么,您会推荐哪种方法?
此外,如果它们有比内部或外部工厂更合适的名称,我想知道它们。
发布于 2011-04-15 06:03:43
实际上,工厂是已建立的创造性设计模式,因此您可以在GoF书籍或Sourcemaking:上了解它们的用途
在这些模式中有一些重叠,特别是在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,并且主要通过该容器来管理对象。
https://stackoverflow.com/questions/5669363
复制相似问题