2011-04-14 3 views
5

Je réfléchis à l'une des deux manières différentes d'implémenter le patron d'usine en PHP. Je ne sais pas si ces variantes ont des noms propres donc pour l'instant je vais les appeler usine interne et usine externe.Usine interne ou externe

usine interne: La méthode de fabrication est mis en œuvre dans la classe comme une méthode publique statique

<?php 
class Foo 
{ 
    protected 
     $loadedProps = false; 

    public static factory ($id) 
    { 
     $class = get_called_class(); 
     $item = new $class ($id); 
     if ($item -> loadedProps()) 
     { 
      return ($item); 
     } 
    } 

    public function loadedProps() 
    { 
     return ($this -> loadedProps); 
    } 

    protected function loadPropsFromDB ($id) 
    { 
     // Some SQL logic goes here 
    } 

    protected function __construct ($id) 
    { 
     $this -> loadedProps = $this -> loadPropsFromDB ($id); 
    } 
} 
?> 

usine externe: L'usine et les éléments initialise sont mis en œuvre comme des entités distinctes

<?php 

class Foo 
{ 
    protected 
     $loadedProps = false; 

    public function loadedProps() 
    { 
     return ($this -> loadedProps); 
    } 

    protected function loadPropsFromDB ($id) 
    { 
     // Some SQL logic goes here 
    } 

    public function __construct ($id) 
    { 
     $this -> loadedProps = $this -> loadPropsFromDB ($id); 
    } 
} 

abstract class FooFactory 
{ 
    public static factory ($id) 
    { 
     $item = new Foo ($id); 
     if ($item -> loadedProps()) 
     { 
      return ($item); 
     } 
    } 
} 
?> 

Maintenant, il me semble que chacun a ses mérites.

Le premier vous permet de cacher le constructeur du monde extérieur. Cela signifie que la seule façon de créer un objet Foo est via l'usine. Si l'état de l'élément ne peut pas être chargé à partir de la base de données, l'usine renverra la valeur NULL que vous pouvez vérifier facilement dans le code.

if ($item = Foo::factory ($id)) 
{ 
    // ... 
} 
else 
{ 
    // The item failed to load. Handle error here 
} 

La fabrique peut également créer des objets de n'importe quelle sous-classe de Foo sans aucune modification.

Cependant, il semble avoir quelques inconvénients. Tout d'abord, la classe doit mettre en place l'usine, ce qui pourrait mettre des responsabilités dans la classe qui appartient vraiment ailleurs. La version interne de l'usine donne certainement une classe plus grande que la version externe de l'usine. En ce qui concerne l'usine externe, c'est plus propre car l'usine n'est pas dans la classe elle-même et je n'ai pas à m'inquiéter d'une classe prenant plus de responsabilités qu'elle ne le devrait. L'usine externe peut également être mieux adaptée à l'injection de dépendance.

Cependant, il a son propre ensemble d'inconvénients. Tout d'abord, le constructeur de l'objet à construire en usine doit être public car PHP n'a pas de concept de package et pas de niveau de protection 'package' pour les membres de la classe. Cela signifie que rien n'empêche un codeur de simplement faire un nouveau Foo() et de contourner l'usine (cela peut cependant faciliter les tests unitaires).

L'autre problème est que FooFactory ne peut créer que des objets Foo, et non aucune de ses sous-classes. Cela pourrait être contourné en ajoutant un autre paramètre à la FooFactory pour spécifier un nom de classe, mais alors l'usine devra faire des vérifications internes que la classe d'objet spécifiée est en fait un descendant de Foo.

Donc, fondamentalement, quels sont les mérites relatifs des deux approches, et que recommanderiez-vous?

En outre, s'ils ont des noms plus propres que l'usine interne ou externe, j'aimerais les connaître.

Répondre

7

En fait, les usines sont établies Design Patterns Creational, vous pouvez donc lire sur leur but dans le GoF livre ou à Sourcemaking:

  • Abstract Factory - crée une instance de plusieurs familles de classes
  • Builder - sépare la construction de l'objet de sa représentation
  • Méthode d'usine - Crée une instance de plusieurs classes dérivées
  • Piscine objet - Évitez l'acquisition et la libération coûteuse des ressources en recyclant les objets qui ne sont plus utilisés
  • Prototype - Une instance entièrement initialisées à copier ou cloner

Il y a un certain chevauchement dans ces modèles, en particulier entre Factory Method, Abstract Factory et Builder et la distinction est encore plus floue lorsque vous ne créez pas des familles d'objets, mais seulement un type d'objet. Alors oui, par simplicité, supposons que les usines internes et externes soient les bons termes.

Personnellement, je préférerais toujours une usine externe sur une usine interne en raison de la raison que vous avez déjà donné: Je peux utiliser Dependency Injection et peut separate the responsibilities. Depuis static methods are death to testability et peut être considered harmful en raison du couplage qu'ils introduisent, je ferais à la place un objet réel à la place et utiliserais des méthodes non statiques.

Les deux inconvénients que vous mentionnez ne sont pas vraiment des inconvénients.

Je ne peux pas penser à une raison pour laquelle je voudrais prevent a developer d'instancier des objets qu'une usine crée. En fait, quand Unit-Testing je vais vouloir créer cet objet par moi-même et remplacer toutes les dépendances par Mocks and Stubs. I also dont believe that developers should be babysitted too much. Compte tenu de la nature de script de PHP, changer le ctor de privé à public est trop facile à éviter efficacement de toute façon. Comme pour l'autre problème de l'usine étant incapable de créer d'autres classes, ce n'est pas vrai. L'idée d'une usine est en fait de créer différents types d'une famille d'objets. Même la méthode d'usine est explicitement autorisée à créer des sous-classes. Que vous l'utilisiez avec un commutateur/boîtier ou avec différentes méthodes en usine, c'est à vous de décider. Il n'y a pas non plus de raison de ne pas combiner Factories avec Builders ou d'avoir des factories of Factories qui encapsulent à leur tour la logique de créer un objet. Ainsi, éliminant le besoin de l'un des contrôles internes que vous mentionnez (qui pourrait également être rencontré avec les typeshints).

L'autre alternative viable à Factories serait d'utiliser un Dependency Injection Container, comme le Symfony Components DIC et de gérer vos objets à travers ce conteneur principalement.

+0

'Hors sujet:' Sur une note aléatoire, je trouve amusant que vous parliez tous les deux de la même chose lorsque vous avez le même nom d'utilisateur. Pendant une seconde, j'ai cru que tu te parlais à toi-même. – Tek

+0

@Tek oui, Gordon répondant à Gordon. Mais je suis Gordon ™ :) – Gordon

Questions connexes