我一直在使用php一段时间了,我总是想知道哪种是在类中存储属性的最佳方法。
第一种方法是将数据存储为属性。
class Foo{
private $id;
private $name;
function __get($var){
return $this->$var;
}
}
另一种方法是将数据存储在一个数组中,然后使用魔术方法将其作为类中的常规变量检索。
class Bar{
private $data;
function __get($var){
return $this->data[$var];
}
}
既然两种方法都会实现相同的目标,哪种方法更好,为什么呢?
答案 0 :(得分:3)
这一切都取决于你想要取得的成就。如果您希望实现任何Traversable
接口,那么将所有属性存储在(关联)数组中将使您的生活更轻松,尽管也有方法可以使用预定义属性执行此操作。
如果最好的话,你的意思是最快:事先定义每个属性将使你的课程更高效as I explained here already
当然,有很多理由说明为什么第一种方法是更好的I've listed them here
基本上,您需要知道的是PHP的“重载”对象的速度很慢( O(1)用于声明的属性vs O(n) 用于重载属性)。魔术方法也是如此,例如__get
和__set
。他们很慢。考虑一下:
class Declared
{
private $foo = 123;
public function setFoo($val)
{
$this->foo = (int) $val;
return $this;
}
public function getFoo()
{
return $this->foo;
}
public function __get($name)
{
$name = 'get'.ucfirst($name);
if (method_exists($this, $name))
{
return $this->{$name}():
}
//return null or throw exception
throw new RuntimeExcpetion('attempted to access non-existing property using '.$name.' in '.__METHOD__);
}
public function __set($name, $val)
{
$mname = 'set'.ucfirst($name);
if (method_exists($this, $mname))
{
return $this->{$mname}($val):
}
throw new RuntimeException($name.' is an invalid property name');
}
}
我不仅可以确定,在任何时候都不会添加新属性,但由于我使用的是自定义getter和setter,我还可以确保每个属性的类型都是我想要/期望的属性是。在这个例子中,我需要$foo
是一个整数,所以在setter中,我将任何作为参数传递给int的值转换为int。你可以用魔法__set
方法做到这一点,但你处理的方法将会变得越来越多(如果是其他的那么多)。
现在将此与此类进行比较:
class NotDeclared
{
private $data = array('foo' => 123);
public function __get($name)
{
return isset($this->data[$name]) ? $this->data[$name] : null;
}
public function __set($name, $value)
{
$this->[$data] = $value;
}
}
现在,这个课看起来确实短得多,所以我可以看出为什么这会吸引任何人。然而,让我们用用法来比较两者:
$declared = new Declared;
$notDeclared = new NotDeclared;
echo $declared->foo;//ok
echo $declared->getFoo();//ok
echo $notDeclared->foo;//ok
$declared->foo = 34;//fine
$declared->setFoo($declared->foo*2);//just fine
echo $declared->fo;//TYPO => exception
echo $notDeclared->fo;//no errors show, but echoes nothing: invisible bug?
//Worse, still: 2 mistakes in one go
$notDeclared->fo = 'Typo, meant foo, but assign string to expected integer property';
$declared->fo = 'asd';//throws excpetion at once
//singe $notDeclared->fo didn't throw excpetions, I'm assuming I reassigned $foo, yet
echo $notDeclared->foo;//ecoes old value
因为我限制__set
的使用,通过在用户尝试分配不存在的属性时抛出激活,任何拼写错误都会立即引发激活。如果你不这样做,你可能最终不得不回溯你的对象的实例,以便发现一个单一的,愚蠢的错字,就像我在这里用fo
更重要的是:当调用魔术方法时,你必须考虑:
$instance->newProperty;
幕后发生了什么?
newProperty
newProperty
方法(语法错误)__get
方法__get
方法
$this->data
)newProperty
键(isset
)这意味着$instance->newProperty
要求PHP查找newPropery
,然后是__get
方法,然后是$data
属性,然后在其中查找newPropery
密钥数组,然后返回null或值。这是在一个物体上进行的大量搜索
这就是为什么我总是会推荐第一个选项。
答案 1 :(得分:0)
这更像是一个OO范式问题。
Magic get函数更多地用作编写getter的懒惰方式。因此,您不必为每个成员编写一个getter。
替代方法是为每个函数编写一个getter,如get_name()
,get_id()
。
第二种方法并不具有可比性,因为您所做的就是将所有成员放入数组中。这无疑是较慢的,因为在某种程度上,当你调用$this->data[$var]
时,php必须遍历你的结构才能找到合适的密钥
总结。 top方法更好,因为它更恰当地模拟了您应该表示的类。第二个会失去类的重点,如果你的唯一成员是一个关联数组,为什么还要有一个类呢?