提问人:RobertPitt 提问时间:9/29/2010 最后编辑:RobertPitt 更新时间:9/29/2010 访问量:2266
方法链接的影响
Effects of method chaining
问:
我知道在PHP中链接的好处,但是假设我们有以下情况
$Mail = new MailClass("mail")
->SetFrom("X")
->SetTo("X")
->SetSubject("X")
->AddRecipient("X")
->AddRecipient("X")
->AddRecipient("X")
->AddRecipient("X")
->AddRecipient("X")
->AddRecipient("X")
->Send();
一遍又一遍地返回和重用对象是否存在任何问题,例如速度或未能遵循最佳实践等问题
如果你是 Fluent-Interface 的新手,这也是一本很好的读物:Martin Fowler on Fluent-Interfaces
我完全理解它不必以这种方式编程,可以这样处理:
$Mail = new MailClass("mail");
$Mail->AddRecipien(
array(/*.....*/)
);
$Mail->SetFrom("X");
$Mail->SetTo("X");
$Mail->SetSubject("X");
$Mail->Send();
但是假设我有一个这样的对象:
$Order = new Order()
->With(22,'TAL')
->With(38,'HPK')->Skippable()
->With(2,'LGV')
->Priority();
请注意,这是此类编程的 Pro 的完美示例->With(38,'HPK')->Skippable()
答:
如果您必须验证某些内容,我认为在 AddRecipient 方法本身中验证它更有意义,但性能应该大致相同。而且我不知道使用方法链接的任何一般缺点。
编辑:更新了答案以匹配问题
函数调用比循环慢,即链接,例如,与调用一个取一个数组的方法相比,该方法在循环中处理该方法会稍微降低性能。addRecipient()
addRecipients()
此外,将更复杂的方法链接到 Fluent API 可能需要对与上次调用的方法相关的数据进行额外的簿记,以便下一次调用可以继续处理该数据,因为所有方法都返回构建链的同一对象。 让我们看一下您的示例:
...
->With(22, 'TAL')
->With(38, 'HPK')->Skippable()
->With(2, 'LGV')
...
这需要你记住,这是要应用于的,而不是 .Skippable()
(38, 'HPK')
(22, 'TAL')
不过,除非您的代码将在循环中非常频繁地调用,或者当您对 Web 服务器有如此多的并发请求以致它接近其极限时,您几乎不会注意到性能损失(重负载网站就是这种情况)。
另一个方面是方法链接模式强制使用异常来发出信号错误(我并不是说这是一件坏事,它只是与经典的“调用和检查函数结果”编码风格不同)。
然而,通常会有一些函数产生它们所属的对象以外的其他值(例如,那些返回对象和访问器状态的函数)。 重要的是,API 的用户可以确定哪些函数是可链接的,哪些不能,而无需在每次遇到新方法时参考文档(例如,指南说所有 mutator 和只有 mutator 都支持链接)。
对原始问题的回答:
[...]我在链接方面遇到的问题是,您无法真正执行额外的验证 [...]
或者,实现一个专用的验证方法,在设置所有属性后调用该方法,并让它返回验证失败的数组(可以是纯字符串或对象,例如名为 ValidationFailure
)。
这是一把双刃剑。
好的一面?这比重新寻址类更干净,尽管它主要只是一个语法更改,但它会加快处理速度。最好是循环这种链,而不是以长格式循环每个调用。
坏的一面?当人们第一次习惯它时,这将导致安全问题。在这方面,要勤奋地净化传入的变量,这样你就不会在那里传递一些你不应该传递的东西。不要让你的课程过于复杂。
- 预先验证您的信息。
- 对用户进行预授权。
我认为将这些方法链接到一个循环中没有问题,从性能上讲。
您不能直接从类实例化链接:
$Mail = new MailClass("mail")
->SetFrom("X")
->SetTo("Y");
您必须先实例化,然后针对实例化的对象进行链接:
$Mail = new MailClass("mail") ;
$Mail->SetFrom("X")
->SetTo("Y");
如果在各个 setter 方法中进行验证(应如此),则需要确保在遇到验证错误时引发异常。你不能简单地在出错时返回一个布尔值 false,否则链接将尝试针对布尔值而不是你的类实例调用下一个方法,你会得到。
致命错误:调用成员函数 SetSubject() 在非对象上 C:\xampp\htdocs\oChainTest.php 上线 23
如果抛出异常,则可以在 try ...抓住
$Mail = new MailClass("mail");
try {
$Mail->SetFrom("X")
->SetTo("Y")
->SetSubject("Z");
} catch (Exception $e) {
echo $e->getMessage();
}
但作为警告,这将使实例处于部分更新状态,对于成功验证/执行的方法,不会回滚(除非您自己编写回滚),并且不会调用异常后的方法。
评论
MailClass::create('mail')->setFrom('X')
Registry::Use("Mail")->X()->Y->Z();
Registry::Use("Mail")
评论
->With()
Order