共享类之间的依赖,同时使依赖注入
我有两个类共享类之间的依赖,同时使依赖注入
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原则。
这似乎是你最好的选择,如果你不得不从无论是创建对象注入你的依赖:
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();
}
谢谢,但正如我在场景1中提到的,如果我有大量的类,然后如果我想要更改依赖项,我必须编辑所有子类。 – Sushant 2015-02-07 08:04:58
这听起来更像是一个设计问题。如果您使用PHP 5.4或更高版本,则可以尝试使用特征来解决此问题。 http://php.net/manual/en/language.oop5.traits.php – Derek 2015-02-07 08:06:28
我很欣赏你对特质的想法。但是,Traits依赖不能在运行时更改,因为我必须使用Getter和Setter,它再次违反IoC原则,因为我无法在运行时切换类 – Sushant 2015-02-07 08:33:01
为了保持清洁,对于具有几个依赖关系的继承场景来说是可维护的,我个人更倾向于构造器注入的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;
}
}
无论你选择的选项,请确保使用它在你的代码库一贯。
我会使用'if(is_null($ this-> A)){...'而不是'if(null === $ this-> A){'但这只是个人偏好 – Derek 2015-02-26 17:35:28
我不知道我理解你正在试图通过继承完成的任务。继承应该用于概念上相同的类型,而不是用于公共依赖注入点。如果你有使用继承的自然情况,并且你也想使用依赖注入,为了遵循良好的实践,那么这是有道理的。但是,如果继承和构造函数参数是要走的路,那么如果有很多继承,就不能在很多类中修改它们。但拥有大量的继承权通常是你过度使用继承的标志。继承常常被滥用。它使得类的行为变得复杂并且难以阅读和改变。总是寻找继承的替代方案,只有在它比替代方案更好地工作时才使用它。
你有另一码味。如果您需要传递足够的构造函数参数来创建可维护性问题,那么您的类可能违反了单一责任原则。这意味着你需要将你的课程分成更多的课程。您可能还需要将某些构造函数参数更改为适当方法的方法参数。
正如其他人所说,你可以使用的特性。这会让你更容易继承你想要的确切行为。这些特质既可以创建属性,也可以使用某种默认类型设置它们,并提供一种获取和设置这些属性的方法。
至于你的事情能够在运行时改变的担忧,我不知道你的意思。它全部在运行时运行。这最终只是变数。可以在运行时调用构造函数,并且可以传递他们需要的任何值。你也可以在运行时使用setter。根据定义,依赖注入是一种运行时技术。如果你的类没有提供通过DI改变类型的方法来创建有问题的类型,那么我认为它在运行时不可改变,但没有人提出这样的建议。
你可以利用工厂类或静态工厂方法。静态工厂方法可以有不同的名称来描述它将如何构建。工厂类可以有一个工厂方法,在这种情况下,您可以将工厂实例设置为任何工厂正确构造对象的工厂实例。
不要忘记默认参数。无论你如何进行依赖注入或构建对象,都应该牢记这些内容。例如:
class someClass {
public function _construct($serviceA = new serviceA()) {
$this->serviceA = $serviceA;
}
}
请问您是否与我们分享您的实际使用案例?我相信取决于'Base'' Child'和依赖关系实际上可能有不同的方法来解决这个问题。 – lukasgeiter 2015-02-22 22:14:51
这听起来像你打破[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
'ClassA,ClassB'他们在哪里? – sectus 2015-02-24 10:06:41