永远不要在构造函数中实例化对象?

时间:2014-07-31 15:31:37

标签: php oop constructor

在构造函数中实例化对象是不是很糟糕?如下所示?

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();
    }
}

如果是这样,它有多糟糕?该如何正确完成?

4 个答案:

答案 0 :(得分:3)

手动实例化另一个类中的类会创建隐式依赖项,这很难维护 - 如果您需要更改那些FooToo,您将很难检测到需要更改的内容类。

因此,管理依赖关系的更好方法是:

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依赖,就像你的代码一样。相反,FooBar类都应该依赖于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,那么一切都不会破裂。

答案 1 :(得分:2)

这本身并不坏。

缺点是它会降低班级的“可测试性”,因为Boo现在依赖于存在FooToo

答案 2 :(得分:2)

这取决于项目的大小。

对于大型项目或长期项目,应略有改变。

理想情况下,您可以重构它实现Dependency Injection pattern,并可能使用Factory来实例化它。

一些示例代码:

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);
    }
}

答案 3 :(得分:1)

这取决于您的要求和课程。 让我们说每次调用Foo / Too的构造函数都会对数据库执行一个巨大的查询来获取数据,在这种情况下我会选择使用lazy instantiation

当然,在构造函数中初始化属性是一种很好的做法,但在现实生活中可以表现出你的敌人。

示例:

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();

您也可以通过静态方法执行此操作。