在内部使用属性时,编写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,但是选项2会更“真实的OOP”imho。
另请阅读http://fabien.potencier.org/article/47/pragmatism-over-theory-protected-vs-private,其中也建议选项2:/
另一个相关问题; 如果属性有一个setter,那么该属性应该是私有的,强制最终开发人员在子类中使用它,还是应该是一个不成文的规则,程序员有责任设置一个有效的属性值?
答案 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时,数据将从数据库刷新。