Gor*_*onM 5 php design-patterns factory
我正在思考在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);
}
}
?>
Run Code Online (Sandbox Code Playgroud)
外部工厂:工厂及其初始化的项目作为单独的实体实施
<?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);
}
}
}
?>
Run Code Online (Sandbox Code Playgroud)
现在在我看来每个都有其优点.
前者允许您隐藏外部世界的构造函数.这意味着您可以通过工厂创建Foo对象的唯一方法.如果无法从DB加载项目的状态,则工厂将返回NULL,您可以在代码中轻松检查.
if ($item = Foo::factory ($id))
{
// ...
}
else
{
// The item failed to load. Handle error here
}
Run Code Online (Sandbox Code Playgroud)
工厂还可以在不做任何修改的情况下创建Foo的任何子类的对象.
但是,它似乎有一些缺点.首先,班级必须实施工厂,这可能会将责任放在真正属于其他地方的班级中.内部工厂版本肯定会产生比外部工厂版本更大的类别.
至于外部工厂,它更清洁,因为工厂不属于班级本身,我不必担心班级承担更多的责任.外部工厂也可能更适合依赖注入.
但是,它有其自身的一些缺点.首先,要在工厂中构建的项目的构造函数必须是公共的,因为PHP没有包的概念,并且没有类成员的"包"保护级别.这意味着没有什么能阻止编码器只是做新的Foo()并绕过工厂(这可能会使单元测试变得更容易).
另一个问题是FooFactory只能创建Foo对象,而不能创建任何子类.这可以通过向FooFactory添加另一个参数来指定类名来解决,但是工厂必须进行内部检查,指定的对象类实际上是Foo的后代.
基本上,这两种方法的相对优点是什么,你会推荐哪一种?
此外,如果他们有比内部或外部工厂更多的专有名称,我想知道他们.
实际上,工厂已经建立了创造性设计模式,因此您可以在GoF Book或Sourcemaking中了解他们的目的:
这些模式中存在一些重叠,特别是在Factory Method,Abstract Factory和Builder之间,当您不创建对象族而只是一种类型的对象时,区别更加模糊.所以,是的,为简单起见,我们假设内部和外部工厂是正确的术语.
就个人而言,由于您已经给出的原因,我总是喜欢外部工厂而不是内部工厂:我可以使用依赖注入并可以分离责任.由于静态方法是死的可测性和可认为是有害的,由于他们引进的耦合,我会做出厂一个真正的对象,而不是虽然并使用非静态方法.
你提到的两个缺点根本不是缺点.
我想不出为什么我想阻止开发人员实例化Factory创建的对象的原因.事实上,当Unit-Testing我想要自己创建该对象并用Mocks和Stubs替换任何依赖项.我也不相信开发人员应该照顾得太多.鉴于PHP的脚本性质,将ctor从私有更改为公共过于容易被有效地阻止.
至于Factory无法创建其他类的另一个问题,那不是真的.工厂的想法实际上是创建各种类型的对象系列.甚至工厂方法也被明确允许创建子类.无论您是使用开关/外壳还是在工厂采用各种方法实现,都取决于您.也没有理由不将工厂与建筑商或拥有工厂的工厂结合起来,这反过来又包含了创建对象的逻辑.因此,无需您提及的任何内部检查(也可以通过类型提示来满足).
工厂的另一个可行替代方案是使用依赖注入容器,如Symfony Components DIC,并通过该容器管理您的对象.