Έγχυση εξάρτησης σε XOOPS
Επισκόπηση
Ενότητα με τίτλο «Επισκόπηση»Το Dependency Injection (DI) είναι ένα μοτίβο σχεδιασμού που επιτρέπει στα στοιχεία να λαμβάνουν τις εξαρτήσεις τους από εξωτερικές πηγές αντί να τις δημιουργούν εσωτερικά. Το XOOPS 4.0 παρουσιάζει τη συμβατή υποστήριξη κοντέινερ PSR-11.
Γιατί Έγχυση Εξάρτησης;
Ενότητα με τίτλο «Γιατί Έγχυση Εξάρτησης;»# Χωρίς DI (Σφιχτός σύνδεσμος)
Ενότητα με τίτλο «# Χωρίς DI (Σφιχτός σύνδεσμος)»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(); }}# Με DI (Χαλαρή σύζευξη)
Ενότητα με τίτλο «# Με DI (Χαλαρή σύζευξη)»class ArticleService{ public function __construct( private readonly ArticleRepositoryInterface $repository, private readonly EventDispatcherInterface $dispatcher ) {}}PSR-11 Δοχείο
Ενότητα με τίτλο «PSR-11 Δοχείο»# Βασική χρήση
Ενότητα με τίτλο «# Βασική χρήση»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}# Διαμόρφωση κοντέινερ
Ενότητα με τίτλο «# Διαμόρφωση κοντέινερ»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(); },];Εγγραφή υπηρεσίας
Ενότητα με τίτλο «Εγγραφή υπηρεσίας»# Αυτόματη καλωδίωση
Ενότητα με τίτλο «# Αυτόματη καλωδίωση»// 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);# Εγχειρίδιο εγγραφής
Ενότητα με τίτλο «# Εγχειρίδιο εγγραφής»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 ],];Constructor Injection
Ενότητα με τίτλο «Constructor Injection»# Προτιμώμενη προσέγγιση
Ενότητα με τίτλο «# Προτιμώμενη προσέγγιση»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; }}Μέθοδος Ένεσης
Ενότητα με τίτλο «Μέθοδος Ένεσης»# Για προαιρετικές εξαρτήσεις
Ενότητα με τίτλο «# Για προαιρετικές εξαρτήσεις»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); }}Σύνδεση διεπαφής
Ενότητα με τίτλο «Σύνδεση διεπαφής»# Ορισμός διεπαφών
Ενότητα με τίτλο «# Ορισμός διεπαφών»interface ArticleRepositoryInterface{ public function findById(int $id): ?Article; public function save(Article $article): void; public function delete(Article $article): void;}# Εφαρμογή Bind
Ενότητα με τίτλο «# Εφαρμογή Bind»return [ ArticleRepositoryInterface::class => XoopsArticleRepository::class,
// Or with factory ArticleRepositoryInterface::class => function (ContainerInterface $c) { return new XoopsArticleRepository( $c->get('database') ); },];Δοκιμή με DI
Ενότητα με τίτλο «Δοκιμή με DI»# Εύκολη κοροϊδία
Ενότητα με τίτλο «# Εύκολη κοροϊδία»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); }}XOOPS Ενσωμάτωση παλαιού τύπου
Ενότητα με τίτλο «XOOPS Ενσωμάτωση παλαιού τύπου»# Γεφύρωση Παλαιού και Νέου
Ενότητα με τίτλο «# Γεφύρωση Παλαιού και Νέου»// 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);}# Wrapping Legacy Handlers
Ενότητα με τίτλο «# Wrapping Legacy Handlers»return [ 'article.handler' => function () { return xoops_getModuleHandler('article', 'mymodule'); },
ArticleRepositoryInterface::class => function (ContainerInterface $c) { return new LegacyArticleRepository( $c->get('article.handler') ); },];Βέλτιστες πρακτικές
Ενότητα με τίτλο «Βέλτιστες πρακτικές»- Inject Interfaces - Βασίζονται σε αφαιρέσεις, όχι σε υλοποιήσεις
- Constructor Injection - Προτιμήστε το constructor έναντι του setter injection
- Ενιαία ευθύνη - Κάθε τάξη πρέπει να έχει λίγες εξαρτήσεις
- Avoid Container Awareness - Οι υπηρεσίες δεν πρέπει να γνωρίζουν για το κοντέινερ
- Διαμόρφωση, Μην κωδικοποιείτε - Χρησιμοποιήστε αρχεία διαμόρφωσης για την καλωδίωση
Σχετική τεκμηρίωση
Ενότητα με τίτλο «Σχετική τεκμηρίωση»- ../07-XOOPS-4.0/Implementation-Guides/PSR-11-Dependency-Injection-Guide - PSR-11 υλοποίηση
- ../03-Module-Development/Patterns/Service-Layer - Μοτίβο σέρβις
- ../03-Module-Development/Best-Practices/Testing - Δοκιμή με DI
- ../07-XOOPS-4.0/XOOPS-4.0-Architecture - Αρχιτεκτονική επισκόπηση