2015-12-18 4 views
1

J'ai une application Laravel et je crée une classe qui importe des données. Les données sont stockées dans environ 15 modèles différents, j'ai donc une classe à gérer chaque car chacune a sa propre implémentation. De plus, d'autres modèles pourraient être ajoutés dans le futur.Conception pour une classe demandant plusieurs classes pour effectuer la même tâche

Je voudrais avoir une ligne de code comme $importer->import() dans une classe Importer, qui prend ensuite toutes les 15 des classes d'implémentation, et appelle leurs méthodes import().

Cependant, la méthode générique $importer->import() ressemblera alors à:

public function __construct(ImporterOne $one, ImporterTwo $two, ImporterThree $three, etc..) 
{ 
    $this->importerOne = $importerOne; 
    $this->importerTwo = $importerTwo; 
    // etc ... 
    $this->importerFifteen = $importerFifteen; 
} 

public function import() 
{ 
    $this->importerOne->import(); 
    $this->importerTwo->importer(); 
    // etc... 
} 

Cela ne semble pas très bien parce que cette classe aura alors 15+ DEPENDANCES tout faire essentiellement la même chose. Et puis pour mettre à jour cela, je vais devoir entrer ici et ajouter/supprimer des dépendances et import() appels de méthode. Donc, ma solution est de déléguer l '«enregistrement» des importateurs à chaque implémentation, plutôt que de laisser la classe Generic Importer responsable. Effectivement, un observateur de toutes sortes. Mais au lieu que le client attache l'observateur au sujet, chaque implémentation se rattache au sujet.

use Importer as GenericImporter 

public class importerOne { 

    public function __construct(SomeModel $model, GenericImporter $importer) 
    { 
     $this->importer = $importer; 
     $this->importer->attach($this); 
    } 
} 

// Do the same for each implementation 

// and then the generic importer 

public class Importer { 

    protected $importables = []; 

    public function attach($import_implementation) 
    { 
     array_push($importables, $import_implementation); 
    } 

    public function import() 
    { 
     foreach($this->importables as $importable) 
     { 
      $importable->import(); 
     } 
    } 

} 

Cela semble agréable et solide. Cependant, le problème est que chaque implémentation utilise maintenant leur propre instance de GenericImporter. Alors, quel est le meilleur moyen d'y parvenir? Dois-je implémenter l'importateur générique en singleton? En outre, à des fins de recherche, cela correspond-il à un certain modèle de conception? Cela ressemble à un ObservorPattern, sauf que chaque observateur s'enregistre lui-même.

Répondre

0

Vous dites que:

le problème est que chaque implémentation utilise désormais leur propre instance du GenericImporter

Je ne pense pas que ce serait un problème parce que GenericImporter est seulement une dépendance que vous injectez dans les implémentations. De plus, la GenericImporter vous passez autour est toujours le même objet, comme il sera adopté par « référence »

EDIT

En ce qui concerne le commentaire ci-dessous, quand vous dites:

lorsqu'une dépendance est résolue via le conteneur IoC, elle n'est pas passant par référence. Il est instanciation d'une nouvelle instance de cette classe

Cela dépend de la façon dont vous faites la liaison dans le conteneur IoC: si vous utilisez instance(), pour lier l'importateur de cette façon:

$importer = new Importer(); 
$this->app->instance(Importer::class, $importer); 

alors la même $importer instance sera résolue à partir du conteneur ioC lorsqu'une dépendance Importer est demandée dans votre application.

FIN EDIT

Quoi qu'il en soit, j'améliorer la conception en ajoutant une interface; quelque chose comme ceci:

//INTERFACE FOR IMPORTABLES 
interface Importable 
{ 
    public function __construct(Model $model); 
    public function import(); 
} 

//IMPLEMENTATION OF IMPORTABLES 
class importerOne implements Importable 
{ 
    public function __construct(SomeModel $model) 
    { 
    } 

    public function import() 
    { 
     //logic 
    } 
} 

//THE IMPORTER CLASS 
public class Importer 
{  
    protected $importables = []; 

    //attach any Importable 
    public function attach(Importable $import_implementation) 
    { 
     array_push($this->importables, $import_implementation) ; 
    } 

    //trigger Importable::import 
    public function import() 
    { 
     foreach($this->importables as $importable) 
     { 
      $importable->import(); 
     } 
    } 
} 

Si pour une raison spécifique que vous ne voulez pas passer la dépendance de Importer à vos importables, pourquoi ne vous attachez pas les importables du client?Quelque chose comme ceci:

$importer = new Importer(); 

$importer->attach($importable_1); 
$importer->attach($importable_2); 
//$importer->attach($importable_n); 

$importer->import(); 

De cette façon, les importateurs ne ont pas besoin que vous les transmettre la dépendance Importer

Selon la façon dont vous avez construit votre importables, vous pouvez aussi envisager la construction et le stockage tous dans un tableau et passer le tableau à l'importateur:

$importer->attachAll($importables_array); 
+0

merci pour la réponse. Cependant, dans Laravel, lorsqu'une dépendance est résolue via le conteneur IoC, elle ne passe pas par référence. Il instancie une nouvelle instance de cette classe (dans ce cas, l'importateur). Il est logique de le configurer dans le code client, mais en fait, j'essaie d'éviter une situation où si/quand les exigences changent et il y a plus d'implémentations, je ne modifie pas constamment les méthodes/classes/code client pour ajouter/supprimer Implémentations – djt

+0

@dtj: pour la première chose vérifier ma modification ci-dessus. Pour la seconde: si vous avez plus d'un point dans votre client où vous devez attacher les imporables à l'importateur, vous feriez mieux d'utiliser le moyen – Moppo

+0

d'observateur pour ce conseil sur l'instance, je ne le savais pas. Je suppose que le dernier aspect de ceci: Fondamentalement, toutes ces implémentations dépendent de la classe Generic Importer pour exécuter leur fonctionnalité. A partir de maintenant, je dois encore instancier chaque implémentation quelque part dans mon code, pour déclencher l'injection de dépendance de l'importateur générique dans chaque implémentation. J'espérais définir fondamentalement toutes les implémentations quelque part (peut-être dans un Service Provider) et quand j'ai appelé $ importer-> import() de quelque part, l'application sait rassembler ces implémentations et les instancier. – djt

0

Peu de choses ... 1) Rappelez-vous ... ça fait des années que j'ai touché PHP ... mais les motifs de design font partie de mon objectif de doctorat ... Mais ... je vous recommande de vous en tenir le plus proche possible des conventions de nommage des patterns (ou au moins documenter les classes/méthodes comme les rôles qu'ils jouent dans un pattern) Parce que j'ai l'impression que vous auriez plus de facilité à implémenter ceci si vous l'avez fait. Vous semblez en fait implémenter un -inverse observer pattern Traditionnellement, l'objet Sujet conserve un tableau d'observateurs qui l'observent (vous aurez juste 1 observateur, mais le modèle en supporte beaucoup), puis appelle "notifyObservers()" qui boucle à travers tous Observer et appelle 'notify()' pour chacun d'eux.Vous semblez essayer d'envoyer la notification e 'autrement'. C'est probablement pourquoi vous obtenez plusieurs instances de 'GenericImporter'. Vous voulez 'importOne' pour 'notifier' l '' importateur 'mais le modèle Observer vous ferait faire l'inverse, vous voudriez que le' importOne ', etc. soit le' Subject '(s) et le' Importateur ' 'être le (seul)' observateur '.

Cette conception peut être un peu moins naturelle dans la façon dont vous voulez que votre programme fonctionne, mais c'est ainsi que le motif est conçu. Vous avez trop "l'observateur". Le 'Observer' est un acteur passif et obtient juste sa méthode 'update (Observable o, Object arg)' appelée quand la classe Observed (Subject) appelle "notifyObservers()". Si vous le concevez de cette façon, votre classe "observateur" peut être TRÈS simple ... (comme l'exemple Java ci-dessous) Tant que vous créez une classe de base qui implémente la fonctionnalité 'Observable' que toutes vos classes Subjects étendent (importOne, importateurTwo, etc.).

[Observer] implémente une méthode/fonction 'notify()'. Ce 'notify()' est ce que l'on appelle quand l'observateur est 'notifié', pas qu'il veut 'notifier' l'autre extrémité.Voici le wiki Java par exemple avec des commentaires détaillés (je vais vous expliquer ce que je ne l'ai pas toucher PHP depuis des années, et peut faire un meilleur travail expliquant l'exemple de Java sur la page wiki)

import java.util.Observable; 
import java.util.Scanner; 

// This is the [Subject](Observed) class 
class EventSource extends Observable implements Runnable { 
    public void run() { 
     while (true) { 
      String response = new Scanner(System.in).next(); 
      setChanged(); 
      notifyObservers(response); 
     } 
    } 
    // Notice that EventSource 'extends' [Observable], which means it has... 
    // the following methods available to it also...(among others)... 
    // 
    // addObserver(Observer o) // adds a new Observer 
    // deleteObserver(Observer o) // deletes a specific Observer 
    // notifyObservers(Object arg) // Notifies all observers with some 'arg' 
    // 
    // Notice that 'notifyObservers( )' is the only one called 
} 

import java.util.Observable; 
import static java.lang.System.out; 

class MyApp { 
    public static void main(String[] args) { 
    out.println("Enter Text >"); 

    // This is the [Subject](Observed class/object) 
    EventSource eventSource = new EventSource(); 

    // here the 'Subject' is registering the observer 
    eventSource.addObserver(
     // This is java 8 notation for a new anonymous class 
     // This creates an instance of the [Observer](link below) Interface 
     // The Observer Object only has one method... 
     // update(Observable o, Object arg){} 
     // The below code is the 'implementation' of the... 
     // 'update(Observable o, Object arg)' method 
     // 'obj' is the object that was being observed, 
     // 'arg' is the object passed into 'notifyObservers(Object)'... 
     // from within the [Subject](observed) object. 
     (Observable obj, Object arg) -> 
     { 
      // This code here is what is called when an Observed object 
      // calls 'notifyObservers()' (in this case only '1' observer) 
      out.println("\nReceived response: " + arg); 
     } 
    ); 
} 

Observer JavaDoc

Espérons que cela vous aidera à ajuster votre programme pour utiliser le modèle d'observateur d'une manière plus traditionnelle, et vous permettra de noter dans votre documentation ce que vous faites/etc.

+0

merci pour la perspicacité, mais mon sujet est la 'GenericImporter' (il est un) et détient un tableau de mes et Observors qui sont ImportOne, ImportTwo, ImportThree (le nombre). Et mon sujet appelle 'import()' - c'est-à-dire notify() - sur chacun d'eux. Cependant, mes Observateurs s'attachent au Sujet, plutôt que d'être attachés dans le code client. Ainsi, chacun a sa propre instance de 'GenericImporter' – djt

+0

@dtj Vous voudrez aussi nettoyer la logique de conception, créer 'importOne', etc. tout étendre une classe parente" ImporterBase "qui implémente l'avis de l'observateur () méthode. De cette façon, vous avez une meilleure conception globale. Cela vous permettra d'étiqueter plus facilement vos classes et de noter le modèle d'observateur. – mawalker

+0

Merci Mais vraiment ce que je demande: est-ce la solution la plus appropriée pour mon problème? – djt