直接调用变量属性vs getter / setters - OOP Design

时间:2011-06-02 13:47:15

标签: php optimization variables getter-setter design-principles

我知道这可能是主观的,但我读了这个optimization page from Google for PHP,他们建议直接使用变量属性而不需要getter和setter。可以理解的是,我看到了这方面的性能提升,但这真的是一个很好的设计实践吗?

他们使用getter / setter的例子:

class dog {
  public $name = '';

  public function setName($name) {
    $this->name = $name;
  }

  public function getName() {
    return $this->name;
  }
}

$rover = new dog();
$rover->setName('rover');
echo $rover->getName();

建议的优化:

$rover = new dog();
$rover->name = 'rover';
echo $rover->name;

在我的设计过程中,这将是一个值得欢迎的变化,因为我认为需要吸气剂/孵化器消失,但这样做会有什么其他障碍/好处?

7 个答案:

答案 0 :(得分:12)

  

这对我来说是一个值得欢迎的改变   设计过程,因为我认为需要   getter / setters消失了,但是什么   其他障碍/利益可能会发生在   这样做?

您无法在特定属性上实现特殊的get / set逻辑。对于标量(字符串,整数,布尔值)的属性,这可能没问题。但是,如果你有一个延迟加载的类实例属性呢?

class Document
{
    protected $_createdBy;

    public function getCreatedBy()
    {
        if (is_integer($this->_createdBy)) {
            $this->_createdBy = UserFactory::loadUserById($this->_createdBy);
        }
        return $this->_createdBy;
    }
}

该技巧仅适用于某种方法。您可以使用__get__set来表示此逻辑,但在添加属性时,最终会出现一个令人讨厌的switch()块:

public function __get($name)
{
    switch ($name) {
        case 'createdBy':
            // blah blah blah
        case 'createdDate':
            // more stuff
        // more case statements until you scream
    }
}

如果您只是想避免或推迟编写getter和setter,请使用__call magic方法来捕获遵循getProperty()setProperty()命名约定的方法调用。您可以将所有默认的获取/设置逻辑放在__call中,而不要再次触摸它:

abstract class Object
{
    public function __call($method, $args)
    {
        $key = '_' . strtolower(substr($method, 3, 1)) . substr($method, 4);
        $value = isset($args[0]) ? $args[0] : null;
        switch (substr($method, 0, 3)) {
            case 'get':
                if (property_exists($this, $key)) {
                    return $this->$key;
                }
                break;

            case 'set':
                if (property_exists($this, $key)) {
                    $this->$key = $value;
                    return $this;
                }
                break;

            case 'has':
                return property_exists($this, $key);
                break;
        }

        throw new Exception('Method "' . $method . '" does not exist and was not trapped in __call()');
    }
}

开发的角度来看,这种方法非常快,因为您只需扩展Object类,定义一些属性,然后就可以参加比赛了:

class Foo extends Object
{
    protected $_bar = 12345;
}

$foo = new Foo();
echo $foo->getBar();  // outputs '12345'
$foo->setBar(67890);  // next call to getBar() returns 67890
$foo->getBaz();       // oops! 'baz' doesn't exist, exception for you

执行的角度来看,这很慢,因为魔术方法很慢,但您可以稍后通过定义明确的getBar()setBar()方法来缓解这种方法(因为{{1}仅在调用未定义的方法时调用。但是如果一个特定的财产不经常被访问,也许你不关心它有多慢。关键是,以后可以很容易地添加特殊的get / set方法,其余的代码永远不会知道差异。

我从Magento处理了这种方法,我发现它非常适合开发人员。在为不存在的属性调用get / set时抛出异常有助于避免由拼写错误引起的幻像错误。在自己的get / set方法中保留特定于属性的逻辑使代码更易于维护。但是你不必在开始时编写所有的访问器方法,你可以轻松地返回并添加它们而无需重构所有其他代码。

问题是,你想要优化什么?开发人员时间或代码速度?如果要优化代码速度,请确保在构建代码之前知道瓶颈所在。过早优化是万恶之源。

答案 1 :(得分:6)

这是某种微优化。从理论上讲,您可以稍后通过使用魔术方法(__get和__set)在名称get / set上添加逻辑,但实际上并不需要这么多。而且,实际上,这种性能提升只有在您拥有其他所有优化的内容时才会很重要,即使只需几微秒就可以增加价值。在这种情况下,您可以使用其他优化技术,例如将所有包含的PHP文件合并为一个,删除类型提示,减少函数参数的数量,使用普通函数而不是类。但通常添加一个简单的缓存可以比所有这些微优化增加10-100倍的性能提升。

答案 2 :(得分:4)

样板答案,我很害怕,但我建议如下: 如果您通过将此属性公开给其他用户而没有对您的类进行封装问题(强制执行业务逻辑等),则完全可以这样做。

答案 3 :(得分:1)

您还可以使用__get和__set魔术方法:

class Example
{
    private $allowedProps = array('prop1', 'prop2', 'prop3');
    private $data = array();

    public function __set($propName, $propValue)
    {
        if (in_array($propName, $this->allowedProps))
        {
            $this->data[$propName] = $propValue;
        }
        else
        {
            // error
        }
    }

    public function __get($propName)
    {
        if (array_key_exists($propName, $this->data))
        {
            return $this->data[$propName];
        }
        else
        {
            // error
        }
    }
}

答案 4 :(得分:1)

起初我很惊讶,我就像...... wtf。但是在我的脑子缠绕几秒后,我意识到这个例子在一个循环中调用了100万次的getter函数。当然,如果变量包含在getter中,我们已经添加了指令,当然这需要更长的时间。

在我看来,在大多数情况下,这是非常微不足道的,因为我还没有找到一个脚本,这个脚本在跑步时会接近100万次吸气。如果 需要将性能压缩到最后一滴,最好知道这种优化技术。

答案 5 :(得分:0)

这取决于$name是否公开。如果不是,则无法直接访问/修改它。权衡是将您班级的内部数据元素直接暴露给集成商。这对某些课程可能没问题,但对其他课程则没有。

例如,您不一定希望其他人能够直接在Product课程中修改产品的价格。

答案 6 :(得分:0)

我想说这实际上是个人偏好的问题。如果表现真的 重要,那么我认为你回答了自己的问题。

但是,在您的第一个示例中,您仍然可以在没有getter / setter的情况下访问dog::name,就像在第二个示例中一样:$rover->name = 'rover';因为$name是公开的。

如果你特别想要隐藏一个类成员,你需要声明变量privateprotected,然后需要一个getter / setter。