我不是PHP开发人员,所以我想知道在PHP中使用显式getter/setter的优点和缺点是什么,在纯OOP风格中,带有私有字段(我喜欢的方式):
class MyClass {
private $firstField;
private $secondField;
public function getFirstField() {
return $this->firstField;
}
public function setFirstField($x) {
$this->firstField = $x;
}
public function getSecondField() {
return $this->secondField;
}
public function setSecondField($x) {
$this->secondField = $x;
}
}
或者只是公共字段:
class MyClass {
public $firstField;
public $secondField;
}
封装在任何面向对象语言中都很重要,与受欢迎程度无关。在动态类型语言(如PHP)中,它特别有用,因为如果不使用设置符,几乎没有办法确保属性是特定类型的。
在PHP中,这是有效的:
class Foo {
public $bar; // should be an integer
}
$foo = new Foo;
$foo->bar = "string";
在Java中,它没有:
class Foo {
public int bar;
}
Foo myFoo = new Foo();
myFoo.bar = "string"; // error
使用魔法方法(__get和__set)也可以,但只有当访问的属性的可见性低于当前作用域可以访问的时候。如果使用不当,调试时很容易让您头疼。
这篇文章不是专门关于__get和__set,而是关于__call,除了方法调用之外,它们是相同的思想。作为一种规则,我远离任何类型的允许重载的魔法方法,原因概述在评论和帖子中,然而,我最近遇到了一个第三方API,我使用了一个服务和一个子服务,例如:
http://3rdparty.api.com?service=APIService.doActionOne&apikey=12341234
这里重要的部分是,这个API除了子操作(在本例中为doActionOne)之外,其他内容都是相同的。这个想法是开发人员(我自己和其他使用这个类的人)可以通过名称调用子服务,而不是像这样:
$myClass->doAction(array('service'=>'doActionOne','args'=>$args));
我可以这样做:
$myClass->doActionOne($args);
对于硬编码来说,这只是大量的重复(这个例子非常类似于代码):
public function doActionOne($array)
{
$this->args = $array;
$name = __FUNCTION__;
$this->response = $this->executeCoreCall("APIService.{$name}");
}
public function doActionTwo($array)
{
$this->args = $array;
$name = __FUNCTION__;
$this->response = $this->executeCoreCall("APIService.{$name}");
}
public function doActionThree($array)
{
$this->args = $array;
$name = __FUNCTION__;
$this->response = $this->executeCoreCall("APIService.{$name}");
}
protected function executeCoreCall($service)
{
$cURL = new \cURL();
return $cURL->('http://3rdparty.api.com?service='.$service.'&apikey='.$this->api.'&'.http_build_query($this->args))
->getResponse();
}
但是使用神奇的__call()方法,我能够使用动态方法访问所有服务:
public function __call($name, $arguments)
{
$this->args = $arguments;
$this->response = $this->executeCoreCall("APIService.{$name}");
return $this;
}
这种动态调用数据返回的好处是,如果供应商添加了另一个子服务,我不必向类中添加另一个方法或创建扩展类等。我不确定这是否对任何人都有用,但我想我会展示一个例子,其中__set, __get, __call等可能是一个考虑的选项,因为主要函数是数据的返回。
编辑:
巧合的是,我在帖子发布几天后看到了这篇文章,它准确地概述了我的场景。这不是我指的API,但方法的应用是相同的:
我是否正确使用api ?
在阅读了其他建议后,我倾向于这样说:
作为GENERIC规则,你不会总是为所有属性定义setter,特别是“内部”属性(信号量、内部标志……)。只读属性显然没有setter,所以有些属性只有getter;这就是__get()缩小代码的地方:
define a __get() (magical global getters) for all those properties which are alike,
group them in arrays so:
they'll share common characteristics: monetary values will/may come up properly formatted, dates in an specific layout (ISO, US, Intl.), etc.
the code itself can verify that only existing & allowed properties are being read using this magical method.
whenever you need to create a new similar property, just declare it and add its name to the proper array and it's done. That's way FASTER than defining a new getter, perhaps with some lines of code REPEATED again and again all over the class code.
是的!我们也可以写一个私有方法来做到这一点,但话又说回来,我们将声明许多方法(++内存),最终调用另一个总是相同的方法。为什么不写一个单一的方法来管理它们呢?(是的!双关语!)):
Magic setter也可以只响应特定的属性,因此可以单独在一个方法中筛选所有日期类型属性的无效值。如果日期类型属性列在数组中,则可以轻松定义它们的setter。当然,这只是一个例子。有太多的情况。
About readability... Well... That's another debate: I don't like to be bound to the uses of an IDE (in fact, I don't use them, they tend to tell me (and force me) how to write... and I have my likes about coding "beauty"). I tend to be consistent about naming, so using ctags and a couple of other aids is sufficient to me... Anyway: once all this magic setters and getters are done, I write the other setters that are too specific or "special" to be generalized in a __set() method. And that covers all I need about getting and setting properties. Of course: there's not always a common ground, or there are such a few properties that is not worth the trouble of coding a magical method, and then there's still the old good traditional setter/getter pair.
编程语言就是人类的人工语言。因此,它们都有自己的语调或重音、语法和风格,所以我不会假装用与Java或c#相同的“重音”来编写Ruby或Python代码,也不会编写类似Perl或SQL的JavaScript或PHP……用他们该用的方式去使用他们。
除了这里已经很好的和受人尊敬的答案之外,我还想扩展一下没有setter /getter的PHP。
PHP没有getter和setter语法。正如Dave所指出的那样,它提供了子类或魔法方法来允许“挂钩”和覆盖属性查找过程。
魔术让我们这些懒惰的程序员在积极参与一个项目并熟悉它的时候,用更少的代码做更多的事情,但通常是以可读性为代价的。
在PHP中,由于强制使用类似getter/setter的代码架构而导致的每个不必要的函数,都会在调用时涉及自己的内存堆栈框架,并浪费CPU周期。
可读性:代码库会导致代码行膨胀,这会影响代码导航,因为更多的LOC意味着更多的滚动。
偏好:个人而言,作为我的经验法则,我接受静态代码分析的失败
作为避免走上这条神奇之路的标志,只要我当时没有明显的长期利益。
谬论:
一个常见的论点是可读性。例如,$someobject->width比$someobject->width()更容易读取。然而,与行星的周长或宽度可以假定为静态的不同,对象的实例(例如$someobject)需要一个width函数,可能需要测量对象的实例宽度。
因此,可读性的提高主要是因为确定的命名方案,而不是通过隐藏输出给定属性值的函数。
__get / __set使用:
属性值的预验证和预清理
字符串。
"
一些{mathsobj1->generatelatex}多
行文本{mathsobj1->latexoutput}
{mathsobj1->generatelatex}有很多变量
一些原因
"
在这种情况下,generatelatex将遵循actionname + methodname的命名方案
特殊的,明显的案例
dnastringobj - >同源框(one_rememberable_parameter美元)- >千钧一发——> findrelated ()
dnastringobj - >同源框(one_rememberable_parameter美元)- > gttccaatttga - > findrelated ()
注意:PHP选择不实现getter/setter语法。我并不是说getter /setter一般都不好。
封装在任何面向对象语言中都很重要,与受欢迎程度无关。在动态类型语言(如PHP)中,它特别有用,因为如果不使用设置符,几乎没有办法确保属性是特定类型的。
在PHP中,这是有效的:
class Foo {
public $bar; // should be an integer
}
$foo = new Foo;
$foo->bar = "string";
在Java中,它没有:
class Foo {
public int bar;
}
Foo myFoo = new Foo();
myFoo.bar = "string"; // error
使用魔法方法(__get和__set)也可以,但只有当访问的属性的可见性低于当前作用域可以访问的时候。如果使用不当,调试时很容易让您头疼。