使用类getter或类属性? (PHP)

时间:2011-12-31 15:51:15

标签: php class properties getter

在内部使用属性时,编写OOP类的最佳做法是什么。

考虑以下课程;

<?php
Class Foo
{
    /**
     * @var null|string
     */
    protected $_foo;

    /**
     * @return null|string
     */
    public function getFoo()
    {
        return $this->_foo;
    }

    protected function _doSomething()
    {
        $foo    = $this->_foo;
        $result = null;
        // ...
        return $result;
    }
}

正如你所见,我在_doSomething()中使用_foo属性,尽管子类可以覆盖getFoo(),返回未存储回_foo的计算值;这是一个缺陷。

我该怎么办?

  1. 将getter标记为final,在内部使用属性(无需额外的函数调用,强制终端开发人员将_foo用作属性,因为它受到保护)
  2. 在内部使用getFoo(),将_foo标记为私有(额外函数调用)
  3. 这两个选项都是防水的,但是我非常关注所有额外的函数调用,所以我倾向于使用选项1,但是选项2会更“真实的OOP”imho。

    另请阅读http://fabien.potencier.org/article/47/pragmatism-over-theory-protected-vs-private,其中也建议选项2:/

    另一个相关问题; 如果属性有一个setter,那么该属性应该是私有的,强制最终开发人员在子类中使用它,还是应该是一个不成文的规则,程序员有责任设置一个有效的属性值?

1 个答案:

答案 0 :(得分:5)

正如你所说,第二种方法是根据OOP更正确的方法。你在调用方法的CPU周期方面比在作为变量访问属性方面花费更多的成本也是对的。但是,在大多数情况下,这将属于微优化的范畴。它不会对性能产生明显影响,除非所涉及的值被大量使用(例如在循环的最内部)。除非表演真正受到影响,否则最佳做法倾向于偏向于最佳表现。

对于简单变量,内部使用getter并不是很明显,但如果你正在处理从外部数据源(如数据库)填充的属性,那么这种技术就会自成一体。使用getter允许您以懒惰的方式从DB中获取数据,即在需要时而不是在需要之前。例如:

class Foo
{
    // All non-relevent code omitted
    protected $data = NULL;

    public class getData ()
    {
        // Initialize the data property
        $this -> data = array ();
        // Populate the data property with a DB query
        $query = $this -> db -> prepare ('SELECT * FROM footable;');
        if ($query -> execute ())
        {
            $this -> data = $query -> fetchAll ();
        }
        return ($this -> data);
    }

    public function doSomethingWithData ()
    {
        $this -> getData ()
        foreach ($this -> data as $row)
        {
            // Do processing here
        }
    }
}

现在使用这种方法,每次调用doSomethingWithData时,结果都是对getData的调用,而getData又调用数据库查询。这很浪费。现在考虑以下类似的类:

class Bar
{
    // All non-relevent code omitted
    protected $data = NULL;

    public class getData ()
    {
        // Only run the enclosed if the data property isn't initialized
        if (is_null ($this -> data))
        {
            // Initialize the data property
            $this -> data = array ();
            // Populate the data property with a DB query
            $query = $this -> db -> prepare ('SELECT * FROM footable;');
            if ($query -> execute ())
            {
                $this -> data = $query -> fetchAll ();
            }
        }
        return ($this -> data);
    }

    public function doSomethingWithData ()
    {
        foreach ($this -> getData () as $row)
        {
            // Do processing
        }
    }
}

在这个版本中,你可以随意调用doSomethingWithData(实际上是getData),你永远不会触发多个数据库查找。此外,如果从不调用getData和doSomethingWithData,则不会进行任何数据库查找。这将带来巨大的性能提升,因为数据库查找很昂贵,应尽可能避免。

如果您在可以更新数据库的类中工作,它确实会导致一些问题,但它并不难解决。如果一个类对其状态进行更新,那么可以简单地对您的setter进行编码,以便它们在成功时将其关联状态置零。这样,下次调用getter时,数据将从数据库刷新。