[英]Is this a sane implementation of constructor injection?
在我对服务定位器提出问题之后, 我决定改用构造函数注入。 请考虑以下代码:
<?php
interface IAppServiceRegistry {
public function getDb();
public function getLogger();
}
interface IFooServiceRegistry extends IAppServiceRegistry {
public function getFooBarBazModel();
}
class AppServiceRegistry
implements IAppServiceRegistry, IFooServiceRegistry
{
private $logger;
private $db;
private $fooBarBazModel;
public function getDb() {
// return db (instantiate if first call)
}
public function getLogger() {
// return logger (instantiate if first call)
}
public function getFooBarBazModel() {
if (!isset($this->fooBarBazModel)) {
$this->fooBarBazModel = new FooBarBazModel( $this->getDb() );
}
return $this->fooBarBazModel;
}
}
// Example client classes:
/**
* Depends on db, logger and foomodel.
*/
class Foo {
private $db;
private $logger;
private $fooModel;
public function __construct(IFooServiceRegistry $services) {
$this->db = $services->getDb();
$this->logger = $services->getLogger();
$this->fooModel = $services->getFooModel();
}
}
/**
* Depends on only db and logger.
*/
class BarBaz {
private $db;
private $logger;
public function __construct(IAppServiceRegistry $services) {
$this->db = $services->getDb();
$this->logger = $services->getLogger();
}
}
然后,随着应用程序的发展,我将在注册表中添加新的服务工厂方法,并在逻辑上适当的地方创建隔离的接口。
这种方法理智吗?
尽管我通常不阅读php,但我认为我大部分都可以理解。 从技术上讲,它看起来不错,但随后您编写
随着应用程序的发展,我将向注册表添加新的服务工厂方法
这往往会损害松散耦合的想法,因为您现在有了专用的服务定位器,而不是通用服务定位器。
向每个类中注入这样的服务注册表违反了单一职责原则 (SRP),因为一旦类可以访问注册表,就很难请求比最初设想的又一个依赖,并且最终将得到God Object 。
最好将所需的依赖项直接注入需要它们的类中。 因此,您的Foo类的构造函数应采用db,记录器和fooModel,而BarBaz类应仅采用db和记录器参数。
接下来的问题可能是:如果我需要很多不同的依赖项来执行工作该怎么办? 这将需要一个带有许多参数的真正丑陋的构造函数,这与其他著名的OO做法背道而驰。
的确如此,但是如果您需要大量依赖关系,则可能违反了SRP,应尝试将设计拆分为更细粒度的对象:)
此实现与您先前向我们展示的服务定位器几乎相同。
要问的一个好问题是,在查看对象的类时,是否了解对象的所有事项才能完成其工作。 就您而言,您仍然不知道。
如果Foo需要数据库,记录器和模型,则可以通过在构造函数中进行查询来弄清楚这一点。
这是一本很好的文章:
http://misko.hevery.com/code-reviewers-guide/flaw-digging-into-collaborators/
我最近一直在努力解决这种问题。 服务定位器与掺杂注入。
我同意Mark的观点,方法是根据需要将单个细粒度对象注入构造函数。 正如Mark所强调的那样,唯一的缺点是,当您构建复杂的对象图时,不可避免地必须从某处开始。 这意味着您的高级对象将注入许多服务(对象)。
解决此问题的简单方法是使用某些方法为您完成艰苦的工作。 最好的例子是Google的Guice,在我看来,这是处理事情的一种非常好的方法。 可悲的是它是为Java编写的! 有关于的PHP版本; 我不确定在这一点上他们中的任何一个都能达到Guice的标准。
我已经写了一篇有关该主题的文章,其中有更详细的内容。 您可能会发现有趣。 它包括依赖项注入框架的简单实现。
底线是,如果您有一个具有许多要求的Foo类,则可以这样创建类:
/**
* Depends on db, logger and foomodel.
*/
class Foo
{
private $db;
private $logger;
private $fooModel;
/**
* (other documentation here)
* @inject
*/
public function __construct(IDbService $db, ILoggerService $logger, $iModelService $model)
{
// do something
}
}
当您想要一个新的Foo对象时,您只需询问依赖注入框架即可创建一个 :
$foo = $serviceInjector->getInstance('Foo');
依赖项注入器将进行艰苦的工作,确保注入了依赖项。 如果可以的话,这包括依赖关系的任何依赖关系。 换句话说,它将以递归方式将其整理到树上。
稍后,当您发现需要IBarService对象时,可以将其添加到Construtor中,而无需更改任何其他代码!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.