应将类属性存储为变量还是存储在数组中

时间:2013-09-27 06:58:35

标签: php

我一直在使用php一段时间了,我总是想知道哪种是在类中存储属性的最佳方法。

第一种方法是将数据存储为属性。

class Foo{
    private $id;
    private $name;

    function __get($var){
        return $this->$var;
    }
}

另一种方法是将数据存储在一个数组中,然后使用魔术方法将其作为类中的常规变量检索。

class Bar{
    private $data;

    function __get($var){
        return $this->data[$var];
    }
}

既然两种方法都会实现相同的目标,哪种方法更好,为什么呢?

2 个答案:

答案 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方法
    1. 查找数据属性($this->data
    2. 检查newProperty键(isset
    3. 的数组
    4. 返回三元的结果(即null)

这意味着$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方法更好,因为它更恰当地模拟了您应该表示的类。第二个会失去类的重点,如果你的唯一成员是一个关联数组,为什么还要有一个类呢?