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

Phi*_*ord 16 php variables optimization design-principles getter-setter

我知道这可能是主观的但是我从谷歌那里读到了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();
Run Code Online (Sandbox Code Playgroud)

建议的优化:

$rover = new dog();
$rover->name = 'rover';
echo $rover->name;
Run Code Online (Sandbox Code Playgroud)

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

squ*_*rel 12

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

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

class Document
{
    protected $_createdBy;

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

这个技巧只适用于一种方法.您可以使用__get__set为此逻辑但是当您添加属性时,最终会出现一个令人讨厌的switch()大块:

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

如果你只是想避免或推迟写getter和setter方法,用__call魔术方法,以下面的陷阱的方法调用getProperty()setProperty()命名约定.您可以将所有默认的get/set逻辑放入,__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()');
    }
}
Run Code Online (Sandbox Code Playgroud)

开发的角度来看,这种方法非常快,因为您只需扩展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
Run Code Online (Sandbox Code Playgroud)

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

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

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


Ale*_*hov 6

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


Mar*_*tin 4

恐怕是一个样板答案,但我建议如下:如果通过将此属性公开给其他用户,您的类没有封装问题(强制业务逻辑等),那么这样做是完全可以的。