2015-02-07 126 views
4

我有两个类共享类之间的依赖,同时使依赖注入

ClassA , ClassB 

类通常取决于两个基本的服务和存储库

ServiceA , ServiceB 

类(ClassA , ClassB)使用DI原则通过构造函数注入依赖。

由于三个共享一些常见的服务如上面提到我希望将所有的一类Base的常用方法和服务,如本

基类

class Base { 

    protected $A; 
    protected $B; 

    public function __construct(ServiceA $A, ServiceB $B){ 
     $this->A = $A; 
     $this->B = $B; 
    } 
} 

儿童服务

class Child extends Base { 

    protected $C;   

    public function __construct(ChildDependency $C){ 
     $this->C = $C; 
    } 

    public function doStuff() 
    { 
     //access the $A (**Its null**) 
     var_dump($this->A); 
    } 

} 

问题

如何在不违反IoC原则的情况下拥有共同的父亲依赖关系?

可能的情况1

我知道我必须叫parent::__construct()初始化基本构造。但是接下来我必须在下面的所有子类中定义Parent的依赖关系。

(但是对于大量的孩子,我必须重复这个过程,它违背了共同DI点的目的)。

class Child extends Base { 

    protected $C;   

    public function __construct(ChildDependency $C, ParentDepen $A, ParentDepn $B){ 
     parent::_contruct($A,$B); 
     $this->C = $C; 
    } 
} 

可能病例2

有使用getter和setter。但我认为他们违反了IoC原则。

+0

请问您是否与我们分享您的实际使用案例?我相信取决于'Base'' Child'和依赖关系实际上可能有不同的方法来解决这个问题。 – lukasgeiter 2015-02-22 22:14:51

+1

这听起来像你打破[SRP](http://en.wikipedia.org/wiki/Single_responsibility_principle)(也许其他一些[SOLID(http://en.wikipedia.org/wiki/SOLID_%28object面向设计%29)原则),以及[拉萨尼亚代码](http://en.wikipedia.org/wiki/Spaghetti_code#Lasagna_code)。 – 2015-02-22 22:45:54

+0

'ClassA,ClassB'他们在哪里? – sectus 2015-02-24 10:06:41

回答

4

这似乎是你最好的选择,如果你不得不从无论是创建对象注入你的依赖:

class Child extends Base { 

    protected $C;   

    public function __construct(ServiceA $A, ServiceB $B, ChildDependency $C){ 
     parent::__contruct($A, $B); 
     $this->C = $C; 
    } 
} 

你可以尝试使用Traits代替:

trait ServiceATrait { 
    public $A = new ServiceA(); 
    public function getServiceA() { return $this->A; } 
} 
trait ServiceBTrait { 
    public $B = new ServiceB(); 
    public function getServiceB() { return $this->B; } 
} 
class Base { 
    use ServiceATrait; 
    use ServiceBTrait; 
} 
class Child extends Base { 
    protected $C;   

    public function __construct(ChildDependency $C) { 
     $this->C = $C; 
    } 
} 

function() { 
    $c = new Child(new ChildDependency()); 
    echo $c->getServiceB()->toString(); 
} 
+0

谢谢,但正如我在场景1中提到的,如果我有大量的类,然后如果我想要更改依赖项,我必须编辑所有子类。 – Sushant 2015-02-07 08:04:58

+0

这听起来更像是一个设计问题。如果您使用PHP 5.4或更高版本,则可以尝试使用特征来解决此问题。 http://php.net/manual/en/language.oop5.traits.php – Derek 2015-02-07 08:06:28

+0

我很欣赏你对特质的想法。但是,Traits依赖不能在运行时更改,因为我必须使用Getter和Setter,它再次违反IoC原则,因为我无法在运行时切换类 – Sushant 2015-02-07 08:33:01

0

为了保持清洁,对于具有几个依赖关系的继承场景来说是可维护的,我个人更倾向于构造器注入的setter注入。 Martin Fowler对此也有一些想法,值得一读。

你也可以选择注入构造函数注入的公共依赖关系,并使用setter注入来注入子依赖关系,以免搞乱构造函数。不过,如果你使用setter注入,确保一个对象没有被部分初始化或缺少一些依赖关系是非常重要的。确保这一点的一个好方法是在服务的getter方法中(尽管您只会在运行时注意到这一点)。德里克扩展例如,你可以定义你的干将如下:

trait ServiceATrait { 
    private $A; 

    public function initServiceA(ServiceA $serviceA) { 
     $this->A = $serviceA; 
    } 

    public function getServiceA() { 
     if (null === $this->A) { 
      throw new \LogicException("ServiceA has not been initialized in object of type " . __CLASS__); 
     } 
     return $this->A; 
    } 
} 

无论你选择的选项,请确保使用它在你的代码库一贯

+1

我会使用'if(is_null($ this-> A)){...'而不是'if(null === $ this-> A){'但这只是个人偏好 – Derek 2015-02-26 17:35:28

0

我不知道我理解你正在试图通过继承完成的任务。继承应该用于概念上相同的类型,而不是用于公共依赖注入点。如果你有使用继承的自然情况,并且你也想使用依赖注入,为了遵循良好的实践,那么这是有道理的。但是,如果继承和构造函数参数是要走的路,那么如果有很多继承,就不能在很多类中修改它们。但拥有大量的继承权通常是你过度使用继承的标志。继承常常被滥用。它使得类的行为变得复杂并且难以阅读和改变。总是寻找继承的替代方案,只有在它比替代方案更好地工作时才使用它。

你有另一码味。如果您需要传递足够的构造函数参数来创建可维护性问题,那么您的类可能违反了单一责任原则。这意味着你需要将你的课程分成更多的课程。您可能还需要将某些构造函数参数更改为适当方法的方法参数。

正如其他人所说,你可以使用的特性。这会让你更容易继承你想要的确切行为。这些特质既可以创建属性,也可以使用某种默认类型设置它们,并提供一种获取和设置这些属性的方法。

至于你的事情能够在运行时改变的担忧,我不知道你的意思。它全部在运行时运行。这最终只是变数。可以在运行时调用构造函数,并且可以传递他们需要的任何值。你也可以在运行时使用setter。根据定义,依赖注入是一种运行时技术。如果你的类没有提供通过DI改变类型的方法来创建有问题的类型,那么我认为它在运行时不可改变,但没有人提出这样的建议。

你可以利用工厂类或静态工厂方法。静态工厂方法可以有不同的名称来描述它将如何构建。工厂类可以有一个工厂方法,在这种情况下,您可以将工厂实例设置为任何工厂正确构造对象的工厂实例。

不要忘记默认参数。无论你如何进行依赖注入或构建对象,都应该牢记这些内容。例如:

class someClass { 
    public function _construct($serviceA = new serviceA()) { 
     $this->serviceA = $serviceA; 
    } 
}