如果我们在这里严格讨论 PHP 而不是 C#、Java 等(编译器将优化这些东西),我发现 getter 和 setter 是一种资源浪费,你只需要代理一个私有字段,什么都不做。
在我的设置中,我创建了两个蹩脚的类,一个有五个私有字段,由五个 getter/setter 对封装,代理该字段(看起来几乎 完全 像 java 代码,很有趣),另一个带有五个公共字段,并在创建实例后最后调用 memory_get_usage()。带有 getter/setter 的脚本使用了 59708 字节的内存,带有公共字段的脚本使用了 49244 字节。
在任何显着大小的类库的上下文中,例如网站框架,这些无用的 getter 和 setter 可以加起来形成一个巨大的内存黑洞。我一直在为我的雇主开发一个 PHP 框架(他们的选择,而不是我的。如果我有选择的话,我不会为此使用它,但话虽如此,PHP 并没有对我们施加任何不可逾越的限制),当我重构时使用公共字段而不是 getter/setter 的类库,整个 shebang 最终每个请求使用的内存至少减少了 25%。
__get()、__set() 和 __call() 'magic' 方法在处理接口更改方面非常出色。当您需要将字段迁移到 getter/setter(或将 getter/setter 迁移到字段)时,它们可以使流程对任何依赖代码透明。使用解释性语言,即使 Eclipse PDT 或 Netbeans 提供了对代码敏感性的相当好的支持,也很难找到字段或方法的所有用法,因此魔术方法对于确保旧接口仍然委托给新接口很有用功能。
假设我们有一个使用字段而不是 getter/setter 开发的对象,我们想将一个名为“field”的字段重命名为“fieldWithBetterName”,因为“field”不合适,或者不再准确描述用途,或者是完全错误的。假设我们想更改一个名为“field2”的字段,以便从数据库中延迟加载其值,因为最初使用 getter 并不知道它...
class Test extends Object {
public $field;
public $field2;
}
变成
class Test extends Object {
public $fieldWithBetterName = "LA DI DA";
private $_field2;
public function getField2() {
if ($this->_field2 == null) {
$this->_field2 = CrapDbLayer::getSomething($this->fieldWithBetterName);
}
return $this->_field2;
}
public function __get($name) {
if ($name == 'field')) {
Logger::log("use of deprecated property... blah blah blah\n".DebugUtils::printBacktrace());
return $this->fieldWithBetterName;
}
elseif ($name == 'field2') {
Logger::log("use of deprecated property... blah blah blah\n".DebugUtils::printBacktrace());
return $this->getField2();
}
else return parent::__get($name);
}
}
$t = new Test;
echo $t->field;
echo $t->field2;
(附带说明,“扩展对象”位只是一个基类,我用于几乎所有具有 __get() 和 __set() 声明的东西,当访问未声明的字段时会引发异常)
您可以使用 __call() 向后退。这个例子很脆弱,但清理起来并不难:
class Test extends Object {
public $field2;
public function __call($name, $args) {
if (strpos($name, 'get')===0) {
$field = lcfirst($name); // cheating, i know. php 5.3 or greater. not hard to do without it though.
return $this->$field;
}
parent::__call($name, $args);
}
}
如果 setter 必须做某事,或者如果 getter 必须延迟加载某事,或确保某事已创建,或其他任何情况,PHP 中的 Getter 和 setter 方法是很好的,但如果它们什么都不做,则它们是不必要且浪费的除了代理字段之外,尤其是使用上面提到的一些技术来管理界面更改。