互连服务的 ZF3 服务 DI 的有效模式
The valid pattern for ZF3 service DI for interconnected services
据我了解,有效模式是:
- 一个实例化所需服务的 FooControllerFactory (FooService)
- 带有构造函数的 FooController __construct(FooService $fooService)
- Controller 获取一些基本数据并从服务中获取结果
- 服务包含所有必需的业务逻辑
这是一项基础服务。最终,这项服务将需要其他服务来进行各种活动。
例如 CacheService、SomeOtherDataService.
The question is, what is a valid/appropriate pattern for
including/injecting those other interconnected services?
我们目前有一个非常简单的真实示例:
拍卖控制器
/**
* get vehicles for specific auction
*/
public function getVehiclesAction ()
{
$auctionService = $this->getAuctionService(); // via service locator
$auctionID = (int) $this->params('auction-id');
$auction = $auctionService->getAuctionVehicle($auctionID);
return $auction->getVehicles();
}
拍卖服务
public function getAuctionVehicles($auctionID) {
$auction = $this->getAuction($auctionID);
// verify auction (active, permissions, ...)
if ($auction) {
$vehicleService = $this->getVehicleService(); // via service locator
$vehicleService->getVehicles($params); // $params = some various conditions or array of IDs
}
return false;
}
车辆服务
public function getVehicles($params) {
$cache = $this->getCache(); // via service locator
$vehicles = $cache->getItem($params);
if (!$vehicles) {
$vehicleDB = $this->getVehicleDB(); // via service locator
$vehicles = $vehicleDB->getVehicles($params);
}
return $vehicles;
}
建议的有效模式示例
拍卖控制器
public function __construct(AuctionService $auctionService) {
$this->auctionService = $auctionService;
}
/**
* get vehicles for specific auction
*/
public function getVehiclesAction ()
{
$auctionID = (int) $this->params('auction-id');
$auction = $this->auctionService->getAuctionVehicle($auctionID);
return $auction->getVehicles();
}
**AuctionService**
public function getAuctionVehicles($auctionID) {
$auction = $this->getAuction($auctionID); // no problem, local function
// verify auction (active, permissions, ...)
if ($auction) {
$vehicleService = $this->getVehicleService(); // we don't have service locator
$vehicleService->getVehicles($params); // $params = some various conditions or array of IDs
}
return false;
}
车辆服务
public function getVehicles($params) {
$cache = $this->getCache(); // we don't have service locator, but cache is probably static?
$vehicles = $cache->getItem($params);
if (!$vehicles) {
$vehicleDB = $this->getVehicleDB(); // where and how do we get this service
$vehicles = $vehicleDB->getVehicles($params);
}
return $vehicles;
}
一些注意事项:
- 服务仅在某些情况下相互关联,在 95% 的情况下它们是独立的
- 拍卖有很多不需要载具的功能
- Vehicle 具有 VehicleController 和 VehicleService,仅在某些情况下与拍卖相关,它是具有其他功能的独立模块
- 在控制器中注入每个需要的服务会浪费资源,因为并不是每个动作都需要它们(在实际应用程序中,我们有更多的互连服务,而不仅仅是两个)
- 在多个服务中编写相同的业务逻辑只是为了避免服务定位器显然是一种无效的模式,是不可接受的。
您可以编写一个新服务,比如 VehicleAuctionService
并使用工厂将 AuctionService
和 VehicleService
作为依赖项注入。
这是对象组合。
class VehicleAuctionService
{
private $auctionService;
private $vehicleService;
public function __construct(
AuctionService $auctionService,
VehicleService $vehicleService
){
$this->auctionService = $auctionService;
$this->vehicleService = $vehicleService;
}
public function getAuctionVehicles($auctionID)
{
$auction = $this->auctionService->getAuction($auctionID);
if ($auction) {
$params = [
'foo' => 'bar',
];
$this->vehicleService->getVehicles($params);
}
return false;
}
}
如果控制器需要太多不同的服务,通常表明控制器的职责太多。
根据@AlexP 的回答,此服务将被注入到您的控制器中。根据您的设置,这肯定会在创建控制器时导致依赖注入级联。这至少会将创建的服务限制为控制器实际需要的服务(以及相关的传递性服务)。
如果这些服务中的某些服务很少需要,并且您担心每次请求都创建它们,新的服务管理器现在也支持 lazy services。这些仍然可以作为常规依赖项注入服务/控制器(如上所述),但仅在第一次调用时创建。
从文档的示例中复制:
$serviceManager = new \Zend\ServiceManager\ServiceManager([
'factories' => [
Buzzer::class => InvokableFactory::class,
],
'lazy_services' => [
// Mapping services to their class names is required
// since the ServiceManager is not a declarative DIC.
'class_map' => [
Buzzer::class => Buzzer::class,
],
],
'delegators' => [
Buzzer::class => [
LazyServiceFactory::class,
],
],
]);
请求服务时,没有立即创建:
$buzzer = $serviceManager->get(Buzzer::class);
但仅在首次使用时:
$buzzer->buz();
通过这种方式,您可以将多个依赖项注入到您的控制器中,并且只会创建实际需要的服务。当然对于任何依赖都是如此,比如其他服务需要的服务等等。
据我了解,有效模式是:
- 一个实例化所需服务的 FooControllerFactory (FooService)
- 带有构造函数的 FooController __construct(FooService $fooService)
- Controller 获取一些基本数据并从服务中获取结果
- 服务包含所有必需的业务逻辑 这是一项基础服务。最终,这项服务将需要其他服务来进行各种活动。 例如 CacheService、SomeOtherDataService.
The question is, what is a valid/appropriate pattern for including/injecting those other interconnected services?
我们目前有一个非常简单的真实示例:
拍卖控制器
/**
* get vehicles for specific auction
*/
public function getVehiclesAction ()
{
$auctionService = $this->getAuctionService(); // via service locator
$auctionID = (int) $this->params('auction-id');
$auction = $auctionService->getAuctionVehicle($auctionID);
return $auction->getVehicles();
}
拍卖服务
public function getAuctionVehicles($auctionID) {
$auction = $this->getAuction($auctionID);
// verify auction (active, permissions, ...)
if ($auction) {
$vehicleService = $this->getVehicleService(); // via service locator
$vehicleService->getVehicles($params); // $params = some various conditions or array of IDs
}
return false;
}
车辆服务
public function getVehicles($params) {
$cache = $this->getCache(); // via service locator
$vehicles = $cache->getItem($params);
if (!$vehicles) {
$vehicleDB = $this->getVehicleDB(); // via service locator
$vehicles = $vehicleDB->getVehicles($params);
}
return $vehicles;
}
建议的有效模式示例
拍卖控制器
public function __construct(AuctionService $auctionService) {
$this->auctionService = $auctionService;
}
/**
* get vehicles for specific auction
*/
public function getVehiclesAction ()
{
$auctionID = (int) $this->params('auction-id');
$auction = $this->auctionService->getAuctionVehicle($auctionID);
return $auction->getVehicles();
}
**AuctionService**
public function getAuctionVehicles($auctionID) {
$auction = $this->getAuction($auctionID); // no problem, local function
// verify auction (active, permissions, ...)
if ($auction) {
$vehicleService = $this->getVehicleService(); // we don't have service locator
$vehicleService->getVehicles($params); // $params = some various conditions or array of IDs
}
return false;
}
车辆服务
public function getVehicles($params) {
$cache = $this->getCache(); // we don't have service locator, but cache is probably static?
$vehicles = $cache->getItem($params);
if (!$vehicles) {
$vehicleDB = $this->getVehicleDB(); // where and how do we get this service
$vehicles = $vehicleDB->getVehicles($params);
}
return $vehicles;
}
一些注意事项:
- 服务仅在某些情况下相互关联,在 95% 的情况下它们是独立的
- 拍卖有很多不需要载具的功能
- Vehicle 具有 VehicleController 和 VehicleService,仅在某些情况下与拍卖相关,它是具有其他功能的独立模块
- 在控制器中注入每个需要的服务会浪费资源,因为并不是每个动作都需要它们(在实际应用程序中,我们有更多的互连服务,而不仅仅是两个)
- 在多个服务中编写相同的业务逻辑只是为了避免服务定位器显然是一种无效的模式,是不可接受的。
您可以编写一个新服务,比如 VehicleAuctionService
并使用工厂将 AuctionService
和 VehicleService
作为依赖项注入。
这是对象组合。
class VehicleAuctionService
{
private $auctionService;
private $vehicleService;
public function __construct(
AuctionService $auctionService,
VehicleService $vehicleService
){
$this->auctionService = $auctionService;
$this->vehicleService = $vehicleService;
}
public function getAuctionVehicles($auctionID)
{
$auction = $this->auctionService->getAuction($auctionID);
if ($auction) {
$params = [
'foo' => 'bar',
];
$this->vehicleService->getVehicles($params);
}
return false;
}
}
如果控制器需要太多不同的服务,通常表明控制器的职责太多。
根据@AlexP 的回答,此服务将被注入到您的控制器中。根据您的设置,这肯定会在创建控制器时导致依赖注入级联。这至少会将创建的服务限制为控制器实际需要的服务(以及相关的传递性服务)。
如果这些服务中的某些服务很少需要,并且您担心每次请求都创建它们,新的服务管理器现在也支持 lazy services。这些仍然可以作为常规依赖项注入服务/控制器(如上所述),但仅在第一次调用时创建。
从文档的示例中复制:
$serviceManager = new \Zend\ServiceManager\ServiceManager([
'factories' => [
Buzzer::class => InvokableFactory::class,
],
'lazy_services' => [
// Mapping services to their class names is required
// since the ServiceManager is not a declarative DIC.
'class_map' => [
Buzzer::class => Buzzer::class,
],
],
'delegators' => [
Buzzer::class => [
LazyServiceFactory::class,
],
],
]);
请求服务时,没有立即创建:
$buzzer = $serviceManager->get(Buzzer::class);
但仅在首次使用时:
$buzzer->buz();
通过这种方式,您可以将多个依赖项注入到您的控制器中,并且只会创建实际需要的服务。当然对于任何依赖都是如此,比如其他服务需要的服务等等。