Spiega il pattern ServiceLocator di ZF2

5

Sto osservando questo esempio

Estratto pertinente dall'alto:

class AlbumController
{ 
    public function getAlbumTable()
    {
        if (!$this->albumTable) {
            $sm = $this->getServiceLocator();

            //* My Question Lies Here: 
            $this->albumTable = $sm->get('Album\Model\AlbumTable');
        }
        return $this->albumTable;
    }
}

Che cos'è $this->albumTable ? Il suo tipo è AlbumTable . Cos'è AlbumTable ? È una classe che gestisce un'istanza di class TableGateway del modulo ZendDb di ZF2.

Il problema

In un certo senso, AlbumController dipende su TableGateway . ServiceLocator rende solo questa catena di dipendenze leggermente più nascosta.

In realtà rende $this->albumTable come una "variabile che contiene dati" e aggiunge la possibilità di "chiamare quella variabile da qualsiasi parte all'interno del codice per un capriccio" - tipo di dichiarare un tipo di dati int , dichiarare TableGateway legato alla tua stessa entità, insieme alla possibilità di aggiungere le tue personali funzioni di accesso e recupero dati aziendali. Concedo che è abbastanza bello ...

E allora? Qual è il mio problema con questo?

Problema: la classe AlbumController contiene la dipendenza su TableGateway .

Volevo cercare maggiori chiarimenti sul perché questo sia accettabile in termini di Principio di Responsabilità Unica e altri simili problemi di separazione OO. Forse sto prendendo un po 'di distanza, ma tutti i bisogni di AlbumController sono i dati che TableGateway può fornire, vale a dire cose come i dati di tipo Album() . Perché non implementare un meccanismo che può ottenere AlbumController dei suoi dati ma senza la dipendenza indiretta da TableGateway? Esistono schemi di progettazione che possono farlo accadere.

Per riepilogare

  • Perché tollerare la dipendenza da TableGateway nella classe Controller?
  • Come è nato ServiceLocator - è apparentemente abituato a "mettere la dipendenza il più lontano possibile pur mantenendola lì". È questo lo scopo di SeviceLocator ?
  • Perché non utilizzare altre strutture OO (fabbriche, costruttori, ecc.) per fornire / popolare il controllore direttamente con i dati di cui ha bisogno (cioè Album ), invece di appesantirlo con TableGateway ?

Spirito della domanda

Qual è lo scopo di ServiceLocator Pattern? Perché è lì perché è necessario quale problema risolve veramente, quando sembra che semplicemente spinga via le dipendenze?

Inoltre, ServiceLocator è una dipendenza in ogni controller ZF2 che estende AbstractController ... Così facendo il nostro AlbumController personalizzato dipende da ServiceLocator e da qualsiasi cosa che ServiceLocator decide di chiamare (ad esempio TableGateway). Puoi quindi chiamare qualsiasi servizio che ti interessa da qualsiasi postazione di controller che desideri. Che cosa è successo all'iniezione di dipendenza e all'inversione dei concetti di controllo?

UPDATE:

Sembra che Zend abbia deprecato ServiceManager::getServiceLocator() dalla versione 3.0.0. Raccomanda di "usare il contenitore passato in fabbrica".

Vedi anche questo post sul blog sulla deprecazione di ServiceLocatorAware

    
posta Dennis 09.10.2015 - 19:55
fonte

2 risposte

0

Includere un Localizzatore di servizio in un Controller o un Servizio è in effetti un anti-pattern , non più utilizzato dalla ZF versione 3. Invece, usa Fabbriche e Container, come il Service Manager di fabbrica di ZF3 .

Anti-pattern, perché, passando $container a un'azione oa un controller, il codice diventa più complicato. Rende il tuo controller / azione dipendente dall'insieme di classi che $container ha accumulato finora. Non hai idea di cosa controller / azione userà il $container per, e quindi stai nascondendo le dipendenze facendo così. Un approccio di seguito espone invece le dipendenze e le indica esplicitamente passandole al metodo __construct di controller / azione.

Il modulo attualmente raccomandato è al di sotto, dove $sm è ora $container , che potrebbe essere un'istanza di ServiceManager di ZF3:

/* Invoked via ZF3 configuration */
class AlbumControllerFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $albumTable = $container->get(AlbumTable::class);
        return new AlbumController($albumTable);
    }
}

class AlbumController
{    
    /** @var AlbumTable */
    private $albumTable;

    /*
     * Here we inject dependency of AlbumTable
     * into AlbumController
     * We are telling AlbumController exactly what to use (AlbumTable)
     * We do not let AlbumController decide what it wants to use
     */
    public function __construct(AlbumTable $albumTable)
    {
        $this->albumTable = $albumTable;
    }

    /*
     * Example use of $albumTable
     */
    public function indexAction()
    {
        return new ViewModel(array(
            'albums' => $this->albumTable->fetchAll()
        ));
    }
}

Se hai bisogno di lazy istanza dei metodi di AlbumTable, dai un'occhiata a Lazy Services del ServiceManager di ZF3 .

    
risposta data 29.01.2018 - 23:40
fonte
0

La documentazione di Symfony 2 condivide lo stesso problema in quanto la maggior parte degli esempi ufficiali utilizza un localizzatore di servizi. Questo perché un localizzatore di servizi è un po 'più facile da dimostrare dell'iniezione di dipendenza, specialmente per i nuovi arrivati.

La documentazione per entrambi i framework mostra anche più azioni all'interno di una singola classe di controller. Ancora una volta, una classe è più facile da spiegare e organizzare rispetto a più classi. Naturalmente se solo un'azione richiede un servizio specifico, allora usando l'iniezione del costruttore si intende che il servizio è costruito per tutte le azioni. I localizzatori di servizi potrebbero in realtà essere più adatti a questo approccio, specialmente se si mantengono snelli i metodi di azione per aggirare i problemi di test.

Tuttavia, sia Zend che Symfony supportano completamente l'iniezione di dipendenza. link .

E anche se non ho usato Zend in produzione, per Symfony 2 ho adottato l'approccio definendo tutti i controller come servizi e consentendo solo un'azione per ogni classe di controller che elimina il problema di iniettare dipendenze non necessarie.

In questo caso particolare bisogna guardare oltre la documentazione introduttiva per vedere di cosa sono realmente capaci i framework.

    
risposta data 10.10.2015 - 15:43
fonte