是否像下面这样在构造函数中实例化对象?
class Foo
{
public function fooMethod() {
return 'foo method';
}
}
class Too
{
public function tooMethod() {
return 'too method';
}
}
class Boo
{
public $foo;
public $too;
public function __construct()
{
$this->foo = new Foo();
$this->too = new Too();
}
}
如果是,能有多糟?应该怎样做才合适呢?
手动在另一个类中实例化类创建隐式依赖关系,这很难维护-如果您需要更改那些Foo
和Too
类,您将很难检测需要更改的内容。
class Foo
{
private $_bar;
function __construct(Bar $bar)
{
$this->_bar = $bar;
}
}
这样,你的对象依赖关系是显式的。这样做的另一个好处是,一些PHP框架(Laravel、Zend、Symfony)允许自动解析依赖项。这意味着,你不需要手动实例化你的对象,只需要通过某种工厂来实例化——就像这样(Laravel):
$foo = App::make('Foo');
一个App
工厂自动检测你的Foo
类依赖与一些反射魔法,并适当地注入它们。其他框架也有类似的功能。
同样,在OOP中有一些通用原则,称为SOLID
,它有助于开发更好的OOP设计。其中一个——D
,代表Dependency Inversion
。它的意思是,您应该避免hard
依赖,就像在您的代码中一样。相反,Foo
和Bar
类都应该依赖于interface
,像这样:
interface BarInterface
{
function doBar();
}
class Bar implements BarInterface
{
function doBar()
{
print "Doing BAR";
}
}
class Foo
{
/**
* @var BarInterface
*/
private $bar;
function __construct(BarInterface $bar)
{
$this->bar = $bar;
}
}
现在,如果你需要用其他东西来改变Bar
类,如果你的替代品也实现了BarInterface
,那么所有的地狱都不会破裂。
这本身并不坏。
缺点是它降低了类的"可测试性",仅仅因为Boo
现在依赖于存在的Foo
和Too
。
这取决于项目的大小。
对于大型项目或长期项目,应该稍微改变一下。
理想情况下,你应该重构它,实现依赖注入模式,也许使用一个工厂来实例化它。
示例代码:
interface FooInterface { function fooMethod(); }
class Foo implements FooInterface { function fooMethod(){return 'Foo';} }
interface TooInterface { function tooMethod(); }
class Too implements FooInterface { function tooMethod(){return 'Too';} }
class Boo
{
public $foo;
public $too;
public function __construct(FooInterface $foo, TooInterface $boo)
{
$this->foo = $foo;
$this->too = $boo;
}
}
class BooFactory
{
public static function create()
{
return new Boo(new Foo, new Too);
}
}
这取决于您的需求和类。假设每次调用Foo/Too的构造函数,你都会对数据库执行一个巨大的查询来获取数据,在这种情况下,我会选择使用延迟实例化。
当然,在构造函数中初始化属性是一个好做法,但在实际的性能中可能是你的敌人。
的例子:
class Boo {
private $foo = null;
private $too = null;
public function __construct() {
//Do something else
}
public function getFoo() {
if (is_null($this->foo)) {
$this->foo = new Foo();
}
return $this->foo;
}
public function getToo() {
if (is_null($this->too)) {
$this->too = new Too();
}
return $this->too;
}
public function aMethodThatUsesFoo() {
$foo = $this->getFoo();
$foo->fooMethod();
}
public function aMethodThatDoesntUsesFoo() {
echo "Hello!, I don't need foo or too to execute this method";
}
}
如果你只使用这个类来执行aMethodThatDoesntUsesFoo()
,它将永远不会调用Foo/Too的构造函数。
$boo = new Boo();
$boo->aMethodThatDoesntUsesFoo();
如果你只执行aMethodThatUsesFoo()
,它将只实例化Foo
$boo = new Boo();
$boo->aMethodThatUsesFoo();