Tiêm phụ thuộc vào XOOPS
Tổng quan
Phần tiêu đề “Tổng quan”Dependency Insert (DI) là một mẫu thiết kế cho phép các thành phần nhận các phần phụ thuộc của chúng từ các nguồn bên ngoài thay vì tạo chúng từ bên trong. XOOPS 4.0 giới thiệu hỗ trợ vùng chứa DI tương thích PSR-11.
Tại sao phải tiêm phụ thuộc?
Phần tiêu đề “Tại sao phải tiêm phụ thuộc?”Không có DI (Khớp nối chặt)
Phần tiêu đề “Không có DI (Khớp nối chặt)”class ArticleService{ private ArticleRepository $repository; private EventDispatcher $dispatcher;
public function __construct() { // Hard dependencies - difficult to test and modify $this->repository = new ArticleRepository(new XoopsDatabase()); $this->dispatcher = new EventDispatcher(); }}Với DI (Khớp nối lỏng lẻo)
Phần tiêu đề “Với DI (Khớp nối lỏng lẻo)”class ArticleService{ public function __construct( private readonly ArticleRepositoryInterface $repository, private readonly EventDispatcherInterface $dispatcher ) {}}Thùng chứa PSR-11
Phần tiêu đề “Thùng chứa PSR-11”Cách sử dụng cơ bản
Phần tiêu đề “Cách sử dụng cơ bản”use Psr\Container\ContainerInterface;
// Get the container$container = \Xmf\Module\Helper::getHelper('mymodule')->getContainer();
// Retrieve a service$articleService = $container->get(ArticleService::class);
// Check if service existsif ($container->has(ArticleService::class)) { // Use the service}Cấu hình vùng chứa
Phần tiêu đề “Cấu hình vùng chứa”use Psr\Container\ContainerInterface;
return [ // Simple class instantiation ArticleRepository::class => ArticleRepository::class,
// Interface to implementation binding ArticleRepositoryInterface::class => ArticleRepository::class,
// Factory function ArticleService::class => function (ContainerInterface $c): ArticleService { return new ArticleService( $c->get(ArticleRepositoryInterface::class), $c->get(EventDispatcherInterface::class) ); },
// Shared instance (singleton) 'database' => function (): XoopsDatabase { return XoopsDatabaseFactory::getDatabaseConnection(); },];Đăng ký dịch vụ
Phần tiêu đề “Đăng ký dịch vụ”Tự động nối dây
Phần tiêu đề “Tự động nối dây”// The container automatically resolves dependencies// when type hints are available
class ArticleController{ public function __construct( private readonly ArticleService $service, private readonly ViewRenderer $renderer ) {}}
// Container creates ArticleController with its dependencies$controller = $container->get(ArticleController::class);Đăng ký thủ công
Phần tiêu đề “Đăng ký thủ công”return [ ArticleService::class => [ 'class' => ArticleService::class, 'arguments' => [ ArticleRepositoryInterface::class, EventDispatcherInterface::class, ], 'shared' => true, // Singleton ],
'article.handler' => [ 'factory' => [ArticleHandlerFactory::class, 'create'], 'arguments' => ['@database'], // Reference other service ],];Tiêm hàm tạo
Phần tiêu đề “Tiêm hàm tạo”Phương pháp ưu tiên
Phần tiêu đề “Phương pháp ưu tiên”final class ArticleService{ public function __construct( private readonly ArticleRepositoryInterface $repository, private readonly EventDispatcherInterface $dispatcher, private readonly LoggerInterface $logger ) {}
public function create(CreateArticleDTO $dto): Article { $this->logger->info('Creating article', ['title' => $dto->title]);
$article = Article::create($dto); $this->repository->save($article); $this->dispatcher->dispatch(new ArticleCreatedEvent($article));
return $article; }}Phương pháp tiêm
Phần tiêu đề “Phương pháp tiêm”Dành cho các phần phụ thuộc tùy chọn
Phần tiêu đề “Dành cho các phần phụ thuộc tùy chọn”class ArticleController{ public function __construct( private readonly ArticleService $service ) {}
public function show(int $id, ?CacheInterface $cache = null): Response { $cacheKey = "article_{$id}";
if ($cache && $cached = $cache->get($cacheKey)) { return $this->render($cached); }
$article = $this->service->findById($id);
$cache?->set($cacheKey, $article, 3600);
return $this->render($article); }}Ràng buộc giao diện
Phần tiêu đề “Ràng buộc giao diện”Xác định giao diện
Phần tiêu đề “Xác định giao diện”interface ArticleRepositoryInterface{ public function findById(int $id): ?Article; public function save(Article $article): void; public function delete(Article $article): void;}Thực hiện ràng buộc
Phần tiêu đề “Thực hiện ràng buộc”return [ ArticleRepositoryInterface::class => XoopsArticleRepository::class,
// Or with factory ArticleRepositoryInterface::class => function (ContainerInterface $c) { return new XoopsArticleRepository( $c->get('database') ); },];Thử nghiệm với DI
Phần tiêu đề “Thử nghiệm với DI”Chế nhạo dễ dàng
Phần tiêu đề “Chế nhạo dễ dàng”class ArticleServiceTest extends TestCase{ public function testCreateArticle(): void { // Create mocks $repository = $this->createMock(ArticleRepositoryInterface::class); $dispatcher = $this->createMock(EventDispatcherInterface::class); $logger = $this->createMock(LoggerInterface::class);
// Inject mocks $service = new ArticleService($repository, $dispatcher, $logger);
// Set expectations $repository->expects($this->once())->method('save'); $dispatcher->expects($this->once())->method('dispatch');
// Test $dto = new CreateArticleDTO('Title', 'Content'); $article = $service->create($dto);
$this->assertInstanceOf(Article::class, $article); }}Tích hợp kế thừa XOOPS
Phần tiêu đề “Tích hợp kế thừa XOOPS”Cầu nối giữa cái cũ và cái mới
Phần tiêu đề “Cầu nối giữa cái cũ và cái mới”// Get service from container in legacy codefunction mymodule_get_articles(int $limit): array{ $container = \Xmf\Module\Helper::getHelper('mymodule')->getContainer(); $service = $container->get(ArticleService::class);
return $service->findRecent($limit);}Gói các trình xử lý kế thừa
Phần tiêu đề “Gói các trình xử lý kế thừa”return [ 'article.handler' => function () { return xoops_getModuleHandler('article', 'mymodule'); },
ArticleRepositoryInterface::class => function (ContainerInterface $c) { return new LegacyArticleRepository( $c->get('article.handler') ); },];Các phương pháp hay nhất
Phần tiêu đề “Các phương pháp hay nhất”- Tiêm giao diện - Phụ thuộc vào sự trừu tượng hóa, không phải việc triển khai
- Tiêm hàm tạo - Thích hàm tạo hơn hàm chèn setter
- Trách nhiệm duy nhất - Mỗi class phải có ít phần phụ thuộc
- Tránh nhận biết về vùng chứa - Các dịch vụ không nên biết về vùng chứa
- Cấu hình, không viết mã - Sử dụng các tệp cấu hình để nối dây
Tài liệu liên quan
Phần tiêu đề “Tài liệu liên quan”- ../07-XOOPS-4.0/Implementation-Guides/PSR-11-Dependency-Injection-Guide - Triển khai PSR-11
- ../03-Module-Development/Patterns/Service-Layer - Mẫu dịch vụ
- ../03-Module-Development/Best-Thực hành/Thử nghiệm - Thử nghiệm với DI
- ../07-XOOPS-4.0/XOOPS-4.0-Architecture - Tổng quan về kiến trúc