【发布时间】:2009-08-11 20:29:03
【问题描述】:
【问题讨论】:
【问题讨论】:
通常,当您与第 3 方 API 通信或方法/成员结构不明确时,这些方法很有用。
假设您正在编写一个通用的 XML-RPC 包装器。由于在下载 WDL 文件之前您不知道可用的方法,因此使用重载是有意义的。
然后,而不是编写以下内容:
$xmlrpc->call_method('DoSomething', array($arg1, $arg2));
你可以使用:
$xmlrpc->DoSomething($arg1, $arg2);
这是一种更自然的语法。
你也可以使用成员重载,就像变量对象的方法重载一样。
您只需要注意一件事:将其仅用于可变结构对象或仅将其用于 getter 和 setter 的语法快捷方式。在您的类中保留 getter 和 setter 以在多个方法中分离业务逻辑是有意义的,但是将其用作快捷方式并没有错:
class ShortcutDemo {
function &__get($name) {
// Usually you want to make sure the method
// exists using method_exists, but for sake
// of simplicity of this demo, I will omit
// that logic.
return call_user_method('get'.$name, $this);
}
function __set($name, &$value) {
return call_user_method('set'.$name, $this, $value);
}
private $_Name;
function &getName() { return $this->_Name; }
function setName(&$value) { $this->_Name = $value; }
}
这样您就可以继续使用您的 getter 和 setter 来验证和设置您的数据,并且仍然使用这样的语法快捷方式:
$shortcut->Name = 'Hello';
【讨论】:
另一个 Andrew 没有提到(或在撰写本文时没有提到)的方法是摆脱 getter 和 setter。不必像这样声明每个 setter 和 getter:
$obj->setName("Chacha");
$obj->setRep(10000000000000000000000);
你可以这样做
$obj->Name = "chacha";
$obj->Rep = 100000000000000000;
第二种方法更自然。
Magic Methods 基本上进一步推进了面向对象编程的思想,以及如何实现工作对外界而言并不重要的思想。通过魔术方法,它允许您随意存储变量,并让其他类以自然的方式设置它们。
示例:我可以将所有用户的帐户首选项存储在一个数组中,这样可以很容易地进行迭代以将其全部推送到会话中。
如果我不为此使用魔法方法,我将不得不制作一堆集合或获取,这意味着编写更多代码,或者允许直接访问数组,这揭示了实现,所以我可以以后别去改了。
相反,使用魔术方法,我只是让他们定期设置变量,然后我在内部处理它。
【讨论】:
__get() 和__set() 作为调用getX、setX 的快捷方式。对象重载使用必须只用于可变结构对象。
当一个类有复杂的 isset 和 unset 规则时,您可以使用它。例如,包含变量 $a 的类可以是对象数组或其他资源,当未设置时,它们必须执行一些其他功能。
虽然我不确定他们为什么允许添加新属性和检索非私有属性,但您可以使用它来更改对象的内部状态,方法是根据属性名称调用其他代码/成员变量被设置。
在某些情况下,这类似于 C++ 中的运算符重载
【讨论】:
消息转发适用于当您组合或聚合对象时不能选择多态性(例如,您正在使用无法控制的库类)。
<?php
// Class A is final, so we can't make subclasses.
final class A
{
public function hello( $callback )
{
echo call_user_func( $callback, 'hello world' );
}
}
// so instead, we make a wrapper class that will take an instance
// of A as an aggregate
class B
{
private $a;
public function __construct( A $a )
{
$this->a = $a;
}
// this mimics inheritance on the aggregate object
// method calls are automatically forwarded to instance of A
// if they are valid
public function __call( $method, $args )
{
if ( method_exists( $this->a, $method ) )
{
return call_user_func_array( array( $this->a, $method ), $args );
}
throw new Exception( "Method [$method] not found." );
}
}
class C extends B
{
// This mimics overriding an "inherited" method
public function hello( $callback )
{
echo call_user_func( $callback, 'bonjour le monde' );
}
}
$a = new A;
$b = new B( $a );
$c = new C( $a );
$b->hello( 'strtoupper' );
$c->hello( 'strtoupper' );
【讨论】:
这个功能实际上是what object oriented programming is all about,在其发明者 Alan Kay 的心目中:对象相互发送消息,并可能对任何类型的消息做出反应。在编译时固定的方法是这个概念的有限(但也更有效)实现。这就是 Kay 的名言“我发明了面向对象的术语,我可以告诉你 C++ 不是我的想法。”来自。
基本上,允许对象对方法调用做出反应,而无需在编译时固定相应的方法,这实现了面向对象的原始、更广泛的定义。大多数现代“动态”语言都以一种或另一种形式支持它。
至于它的好处:看看Groovy's Builders 的一个很好的例子。基本上,它通过将方法名称本身转换为数据来实现非常紧凑的低冗余语法。
【讨论】:
我使用过的一种方法是创建一个类似于对象关系管理 (ORM) 的 Linq 系统,这有点儿花哨。然后,您可以在其中加载数据库表结构并(从数据库表中)操作数据,就好像它只是一个对象一样。
即
include('blibrary/bLinq.class.inc');
$linq = new bLinq(new bLinqSql('mysql://dsn'));
$r = $linq->from('Users')
->password
->select();
转换为以下 SQL:
SELECT `password` from Users;
select语句中的password来自于重载的方法。
结果可以这样使用:
(array)$r->password; // which outputs an array multiple results of password;
(string)$r->password; // which outputs a string of the first password hash;
$r->password[2]; // which outputs a string of the third password hash;
关键是在编程时,“password”这个词可以动态替换数据库中的任何其他字段。
【讨论】:
我使用 __get 和 __set 将对象链接在一起,例如
$user = new User();
echo $user->Profile->views;
这(通常)调用一些 SQL 链接 users.id = profile.user_id。
【讨论】:
属性(如 Python 或 C# 中的属性)。例如,当您使用类似 in Nette 的东西时,您会创建一些类,它将某些属性显示为公共:
<?php
class Foo extends Object
{
public $bar;
}
您可以通过自然方式访问此属性 - $instance->bar。但是当你想做一些验证等时,你只需添加 getter 和 setter:
<?php
class Foo extends Object
{
private $bar;
public function getBar()
{
return $this->bar;
}
public function setBar($bar)
{
if ($bar === NULL) throw new Exception('…');
$this->bar = $bar;
}
}
你仍然使用$instance->bar。但是当你调用$instance->bar = NULL; 时,就像调用$instance->setBar(NULL);。
【讨论】: