我正在考虑在 PHP 中实现工厂模式的两种不同方法中的一种。我不知道这些变体是否有合适的名称,所以现在我将称它们为内部工厂和外部工厂。
内部工厂:工厂方法作为静态公共(public)方法在类本身中实现
<?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 对象的唯一方法是通过工厂。如果无法从数据库加载项目的状态,则工厂将返回 NULL,您可以在代码中轻松检查。
if ($item = Foo::factory ($id))
{
// ...
}
else
{
// The item failed to load. Handle error here
}
工厂还可以创建 Foo 的任何子类的对象而无需任何修改。
不过,它似乎也有一些缺点。首先,类必须实现工厂,这可能会将真正属于其他地方的责任放在类中。内部工厂版本肯定会产生比外部工厂版本更大的类。
至于外部工厂,它更干净,因为工厂本身不在类中,而且我不必担心类会承担超出其应有的责任。外部工厂也可能更适合依赖注入(inject)。
但是,它也有其自身的一系列缺点。首先,要在工厂中构建的项目的构造函数必须是公共(public)的,因为 PHP 没有包的概念,也没有类成员的“包”保护级别。这意味着没有什么可以阻止编码人员只执行 new Foo () 并绕过工厂(尽管这可能会使单元测试更容易)。
另一个问题是 FooFactory 只能创建 Foo 对象,不能创建它的任何子类。这可以通过向 FooFactory 添加另一个参数来指定类名来解决,但是工厂将必须进行内部检查以确保指定的对象类实际上是 Foo 的后代。
基本上,这两种方法的相对优点是什么,您会推荐哪种?
另外,如果他们有比内部或外部工厂更多的专有名称,我想知道他们。
最佳答案
实际上,工厂是创建型设计模式,因此您可以在 GoF Book 或 Sourcemaking: 中了解它们的用途。
这些模式有一些重叠,尤其是在工厂方法、抽象工厂和构建器之间,当您创建的不是对象系列,而是一种类型的对象时,区别就更加模糊了。所以是的,为简单起见,我们假设内部和外部工厂是正确的术语。
就个人而言,由于您已经给出的原因,我总是更喜欢外部工厂而不是内部工厂:我可以使用 Dependency Injection并且可以separate the responsibilities .自 static methods are death to testability可以是considered harmful由于它们引入的耦合,我会将 Factory 设为一个真实的对象并使用非静态方法。
你说的这两个缺点根本算不上缺点。
我想不出我想要 prevent a developer 的原因从实例化工厂创建的对象。事实上,当Unit-Testing我想自己创建该对象并用 Mocks and Stubs 替换所有依赖项. I also dont believe that developers should be babysitted too much .鉴于 PHP 的脚本性质,将 ctor 从私有(private)更改为公共(public)太容易了,无论如何都无法有效阻止。
至于工厂无法创建其他类的另一个问题,那是不正确的。工厂的想法实际上是创建各种类型的对象族。甚至工厂方法也被明确允许创建子类。是否使用开关/案例或工厂中的各种方法来实现它取决于您。也没有理由不将 Factories 与 Builders 结合起来,或者让 Factories of Factorys 反过来封装创建对象的逻辑。从而消除了您提到的任何内部检查的需要(也可以通过类型提示来满足)。
工厂的另一个可行替代方案是使用 Dependency Injection Container , 比如 Symfony Components DIC并主要通过该容器管理您的对象。
关于php - 内部工厂与外部工厂,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5669363/
在railstutorial中,作者为什么选择使用这个(代码list10.25):http://ruby.railstutorial.org/chapters/updating-showing-and-deleting-usersnamespace:dbdodesc"Filldatabasewithsampledata"task:populate=>:environmentdoRake::Task['db:reset'].invokeUser.create!(:name=>"ExampleUser",:email=>"example@railstutorial.org",:passwo
我理解(我认为)Ruby中类变量和类的实例变量之间的区别。我想知道如何从该类外部访问该类的实例变量。从内部(即在类方法中而不是实例方法中),它可以直接访问,但是从外部,有没有办法做MyClass.class.[@$#]variablename?我没有任何具体原因要这样做,只是学习Ruby并想知道是否可行。 最佳答案 classMyClass@my_class_instance_var="foo"class上述yield:>>foo我相信Arkku演示了如何从类外部访问类变量(@@),而不是类实例变量(@)。我从这篇文章中提取了上述内
我在我的Rails项目中使用rspec_rails和factory_girl_railsgem。所有模型都已创建。是否有我可以运行的生成器来为现有模型创建工厂文件?例如:我已经有了一个Blog模型。RSpec允许我通过简单地运行以下命令在spec/models/blog_spec.rb生成一个模型规范文件:railsgeneraterspec:modelblog是否有我可以在命令行中运行的生成器,它会为这个现有模型生成工厂文件,位于:spec/factories/blogs.rb?我在factory_girl_rails中没有看到任何关于发电机的提及文档。
在这段Ruby代码中:ModuleMClassC当我尝试运行时出现“'M:Module'的未定义方法'helper'”错误c=M::C.new("world")c.work但直接从另一个类调用M::helper("world")工作正常。类不能调用在定义它们的同一模块中定义的模块函数吗?除了将类移出模块外,还有其他解决方法吗? 最佳答案 为了调用M::helper,你需要将它定义为defself.helper;结束为了进行比较,请查看以下修改后的代码段中的helper和helper2moduleMclassC
我想在Windows7上安装带有ruby1.9.3的rspec-railsgem。我收到一些错误消息,提示无法安装某些json库。所以,我使用下面的说明来解决它。来源=The'json'nativegemrequiresinstalledbuildtools从[rubyinstaller.org][3]下载[Ruby1.9.3][2]从[rubyinstaller.org][3]下载DevKit文件对于Ruby1.9.3,使用[DevKit-tdm-32-4.5.2-20110712-1620-sfx.exe][4]将DevKit解压到路径C:\Ruby193\DevKit运行cd
是否有可能以某种方式访问Class.new范围内的a?a=5Class.new{defb;aend}.new.b#NameError:undefinedlocalvariableormethod`a'for#:0x007fa8b15e9af0>#:in`b' 最佳答案 即使@MarekLipka的回答是正确的——改变变量范围总是有风险的。这是可行的,因为每个block都带有创建它的上下文,因此您的局部变量a突然变得不那么局部了——它变成了一个“隐藏的”全局变量:a=5object=Class.new{define_method(
来自Java,我正在尝试在Ruby中实现LinkedList。我在Java中实现它的通常方法是有一个名为LinkedList的类和一个名为Node的私有(private)内部类,其中LinkedList的每个对象都作为Node对象。classLinkedListprivateclassNodeattr_accessor:val,:nextendend我不想将Node类暴露给外部世界。然而,通过Ruby中的这个设置,我可以使用这个访问LinkedList类之外的私有(private)Node类对象-node=LinkedList::Node.new我知道,在Ruby1.9中,我们可以使用
如果我构建了一个应用程序来访问来自Gmail、Twitter和Facebook的一些数据,并且我希望用户只需输入一次他们的身份验证信息,并且在几天或几周后重置,那会怎样是在Ruby中动态执行此操作的最佳方法吗?我看到很多人只是拥有他们客户/用户凭证的配置文件,如下所示:gmail_account:username:myClientpassword:myClientsPassword这看起来a)非常不安全,b)如果我想为成千上万的用户存储此类信息,它就无法工作。推荐的方法是什么?我希望能够在这些服务之上构建一个界面,因此每次用户进行交易时都必须输入凭据是不可行的。
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我来自C、php和bash背景,很容易学习,因为它们都有相同的C结构,我可以将其与我已经知道的联系起来。然后2年前我学了Python并且学得很好,Python对我来说比Ruby更容易学。然后从去年开始,我一直在尝试学习Ruby,然后是Rails,我承认,直到现在我还是学不会,讽刺的是那些打着简单易学的烙印,但是对于我这样一个老练的程序员来说,我只是无法将它
我在尝试为我在项目中定义的某些对象和关联创建工厂时遇到了问题。我有一种循环关联,其中一个对象与另外两个随后连接在一起的对象关联。+--------------++-------------+|||||TestCase+--------->|TestDataGrid|||||+------+-------++------+------+||||||vv+--------------++--------------+|||||||||TestVariable||TestDataSet|||||+------+-------++------+-------+|||||||||+-------