这在我的设计过程中将是一个可喜的变化,因为我看到对 getter/setter 的需求正在消失,但是这样做可能会出现哪些其他障碍/好处?
您失去了在特定属性上实现特殊 get/set 逻辑的能力。对于标量(字符串、整数、布尔值)的属性,这可能没问题。但是如果你有一个延迟加载类实例的属性呢?
class Document
{
protected $_createdBy;
public function getCreatedBy()
{
if (is_integer($this->_createdBy)) {
$this->_createdBy = UserFactory::loadUserById($this->_createdBy);
}
return $this->_createdBy;
}
}
该技巧仅适用于方法。您可以使用__get
and__set
来处理这个逻辑,但是当您添加属性时,您最终会遇到一个令人讨厌的switch()
大块:
public function __get($name)
{
switch ($name) {
case 'createdBy':
// blah blah blah
case 'createdDate':
// more stuff
// more case statements until you scream
}
}
如果您只是想避免或推迟编写 getter 和 setter,请使用魔法方法来捕获遵循和命名约定__call
的方法调用。您可以将所有默认的 get/set 逻辑放入其中,并且永远不要再触摸它:getProperty()
setProperty()
__call
abstract class Object
{
public function __call($method, $args)
{
$key = '_' . strtolower(substr($method, 3, 1)) . substr($method, 4);
$value = isset($args[0]) ? $args[0] : null;
switch (substr($method, 0, 3)) {
case 'get':
if (property_exists($this, $key)) {
return $this->$key;
}
break;
case 'set':
if (property_exists($this, $key)) {
$this->$key = $value;
return $this;
}
break;
case 'has':
return property_exists($this, $key);
break;
}
throw new Exception('Method "' . $method . '" does not exist and was not trapped in __call()');
}
}
从开发的角度来看,这种方法非常快,因为您可以扩展 Object 类,定义一些属性,然后您就可以参加比赛了:
class Foo extends Object
{
protected $_bar = 12345;
}
$foo = new Foo();
echo $foo->getBar(); // outputs '12345'
$foo->setBar(67890); // next call to getBar() returns 67890
$foo->getBaz(); // oops! 'baz' doesn't exist, exception for you
从执行的角度来看它很慢,因为魔术方法非常慢,但是您可以稍后通过定义显式getBar()
和setBar()
方法来缓解这种情况(因为__call
仅在调用未定义的方法时才会调用)。但是如果一个特定的属性不经常被访问,也许你不在乎它有多慢。关键是,稍后添加特殊的 get/set 方法很容易,而您的其余代码永远不会知道其中的区别。
我从 Magento 抄袭了这种方法,我发现它对开发人员非常友好。在为不存在的属性调用 get/set 时抛出异常有助于避免由拼写错误引起的幻像错误。在其自己的 get/set 方法中保留特定于属性的逻辑使代码更易于维护。但是您不必在开始时编写所有访问器方法,您可以轻松地返回并添加它们,而无需重构所有其他代码。
问题是,您要优化什么?开发时间还是代码速度?如果您想优化代码速度,请确保在围绕瓶颈构建代码之前了解瓶颈所在。过早的优化是万恶之源。