Article de reference

Modèle de méthode de fabrication

En programmation orientée objet , le modèle de conception « méthode de fabrique » utilise des méthodes de fabrique pour créer des objets sans avoir à spécifier leurs classes exa...

En programmation orientée objet , le modèle de conception « méthode de fabrique » utilise des méthodes de fabrique pour créer des objets sans avoir à spécifier leurs classes exactes . Au lieu d'appeler un constructeur , on invoque une méthode de fabrique pour créer un objet. Les méthodes de fabrique peuvent être définies dans une interface et implémentées par les sous-classes, ou implémentées dans une classe de base et éventuellement redéfinies par les sous-classes. Il s'agit de l'un des 23 modèles de conception classiques décrits dans l'ouvrage « Design Patterns » et il est classé comme modèle de création .

Comment les sous-classes d'un objet peuvent-elles redéfinir son implémentation ultérieure et distincte ? Ce modèle consiste à créer une méthode de fabrique au sein de la superclasse qui délègue la création de l'objet à la méthode de fabrique d'une sous-classe.
  • Comment différer l'instanciation d'un objet à une sous-classe ? En créant l'objet via une méthode de fabrique plutôt qu'en appelant directement le constructeur.
  • Cela permet la création de sous-classes qui peuvent modifier la façon dont un objet est créé (par exemple, en redéfinissant la classe à instancier).

    Définition

    Selon Design Patterns : Elements of Reusable Object-Oriented Software : « Définissez une interface pour créer un objet, mais laissez les sous-classes décider quelle classe instancier. La méthode Factory permet à une classe de différer l’instanciation aux sous-classes. »

    La création d'un objet requiert souvent des processus complexes qu'il est inapproprié d'intégrer à un objet composant. Cette création peut engendrer une duplication importante de code, nécessiter des informations inaccessibles à l'objet composant, ne pas offrir un niveau d'abstraction suffisant ou, pour d'autres raisons, ne pas relever des préoccupations de l'objet composant . Le patron de conception « méthode de fabrique » résout ces problèmes en définissant une méthode distincte pour la création des objets, que les sous-classes peuvent ensuite redéfinir pour spécifier le type dérivé du produit à créer.

    Le modèle de conception Factory Method repose sur l'héritage, car la création d'objets est déléguée aux sous-classes qui implémentent la méthode Factory pour créer des objets. Le modèle peut également s'appuyer sur l'implémentation d'une interface .

    Structure

    Diagramme de classes UML

    Un exemple de diagramme de classes UML pour le modèle de conception Factory Method.

    Dans le diagramme de classes UML ci-dessus , la classe qui requiert un objet n'instancie pas directement cette classe. Elle fait plutôt référence à une instance distincte pour créer un objet produit, ce qui la rend indépendante de la classe concrète exacte instanciée. Les sous-classes peuvent redéfinir la classe à instancier. Dans cet exemple, la sous-classe implémente l'interface abstraite en instanciant la classe.CreatorProductProduct1CreatorfactoryMethod()CreatorCreatorCreator1factoryMethod()Product1

    Exemples

    Structure

    Un jeu de labyrinthe peut se jouer selon deux modes : l’un avec des salles classiques reliées uniquement entre elles, et l’autre avec des salles magiques permettant aux joueurs d’être transportés aléatoirement.

    RoomLa classe de base d'un produit final ( MagicRoomou OrdinaryRoom) MazeGamedéclare la méthode de fabrique abstraite permettant de produire ce produit de base. MagicRoomet OrdinaryRoomsont des sous-classes du produit de base implémentant le produit final. MagicMazeGameet OrdinaryMazeGamesont des sous-classes MazeGameimplémentant la méthode de fabrique produisant les produits finaux. Les méthodes de fabrique découplent ainsi les appelants ( MazeGame) de l'implémentation des classes concrètes. Cela rend l' newopérateur redondant, permet de respecter le principe ouvert-fermé et rend le produit final plus flexible en cas de modification.

    Exemples d'implémentation

    Cette implémentation C++23 est basée sur l'implémentation antérieure à C++98 présentée dans le livre Design Patterns .

    , this ); } };// implémente l'interface Product. class ConcreteProductYOURS : public Product { public : void print () { std :: println ( "this={} print YOURS" , this ); } };// Déclare la méthode de fabrique, qui renvoie un objet de type Product. class Creator { public : virtual unique_ptr < Product > create ( ProductId id ) { switch ( id ) { case ProductId :: MINE : return std :: make_unique < ConcreteProductMINE > (); case ProductId :: YOURS : return std :: make_unique < ConcreteProductYOURS > (); // répéter pour les produits restants default : return nullptr ; } }virtuel ~ Créateur () = par défaut ; };int main ( int argc , char * argv [ ] ) { unique_ptr <Creator> creator = std :: make_unique <Creator> ( ); unique_ptr <Product> product = creator- > create ( ProductId :: MINE ) ; product- > print ( ) ;produit = créateur -> créer ( ProductId :: VOTRE ); produit -> imprimer (); }

    Le résultat du programme ressemble à

    C#
    ; } }public class CityPerson : IPerson { public string GetName () { return "Personne de la ville" ; } }public enum PersonType { Rural , Urban }/// <summary> /// Implémentation de la fabrique - Utilisée pour créer des objets. /// </summary> public class PersonFactory { public IPerson GetPerson ( PersonType type ) { switch ( type ) { case PersonType . Rural : return new Villager (); case PersonType . Urban : return new CityPerson (); default : throw new NotSupportedException (); } } }

    Le code ci-dessus illustre la création d'une interface appelée IPersonet de deux implémentations appelées Villageret CityPerson. Selon le type passé à l' PersonFactoryobjet, l'objet concret d'origine est renvoyé sous forme d'interface IPerson.

    Une méthode de fabrique est simplement un ajout à la PersonFactoryclasse. Elle crée l'objet de la classe via des interfaces, mais permet également à la sous-classe de décider quelle classe est instanciée.

    ; }public bool SetPrice ( double prix ) { _prix = prix ; return vrai ; } }/* Presque identique à Factory, avec une exposition supplémentaire pour interagir avec la méthode créée */ public abstract class ProductAbstractFactory { protected abstract IProduct MakeProduct ();public IProduct GetObject () // Implémentation de la méthode de fabrique. { return this.MakeProduct (); } }public class PhoneConcreteFactory : ProductAbstractFactory { protected override IProduct MakeProduct () { IProduct product = new Phone (); // Faire quelque chose avec l'objet après l'avoir reçu product . SetPrice ( 20.30 ); return product ; } }

    Dans cet exemple, MakeProductest utilisé dans concreteFactory. Par conséquent, MakeProduct()peut être invoqué afin de le récupérer depuis IProduct. Une logique personnalisée pourrait être exécutée après l'obtention de l'objet dans la méthode de fabrique concrète. GetObjectest rendu abstrait dans l'interface de fabrique.

    Cet exemple Java est similaire à celui du livre Design Patterns .

    Cette méthode utilise la MazeGameclasse `use`, Roommais délègue la responsabilité de la création Roomdes objets à ses sous-classes qui créent ensuite les classes concrètes. Le mode de jeu standard pourrait utiliser cette méthode de modèle :

    méthode modèle qui ajoute une logique commune. Il fait référence à la makeRoom()méthode de fabrique qui encapsule la création de salles afin que d'autres salles puissent être utilisées dans une sous-classe. Pour implémenter l'autre mode de jeu avec des salles magiques, cette makeRoomméthode peut être redéfinie.

    PHP

    Cet exemple PHP illustre l'implémentation d'interfaces plutôt que l'héritage (bien que le même résultat puisse être obtenu par héritage). La méthode de fabrique peut également être définie publicet appelée directement par le code client (contrairement à l'exemple Java précédent).

    makeCar(); print $car->getType(); "
    /* Interfaces usine et véhicule */interface CarFactory { fonction publique makeCar () : Car ; }interface Car { fonction publique getType () : chaîne de caractères ; }/* Mises en œuvre concrètes de l'usine et de la voiture */classe SedanFactory implémente CarFactory { fonction publique makeCar () : Car { return new Sedan (); } }classe Berline implémente Voiture { fonction publique getType () : chaîne { return 'Berline' ; } }/* Client */$factory = new SedanFactory (); $car = $factory- > makeCar (); print $car- > getType ();

    Cet exemple Python utilise la même méthode que l'exemple Java précédent.

    None: self.rooms = [] self._prepare_rooms() def _prepare_rooms(self) -> None: room1 = self.make_room() room2 = self.make_room() room1.connect(room2) self.rooms.append(room1) self.rooms.append(room2) def play(self) -> None: print(f\"Playing using {self.rooms[0]}\") @abstractmethod def make_room(self): raise NotImplementedError(\"You should implement this!\") class MagicMazeGame(MazeGame): def make_room(self) -> \"MagicRoom\": return MagicRoom() class OrdinaryMazeGame(MazeGame): def make_room(self) -> \"OrdinaryRoom\": return OrdinaryRoom() class Room(ABC): def __init__(self) -> None: self.connected_rooms = [] def connect(self, room: \"Room\") -> None: self.connected_rooms.append(room) class MagicRoom(Room): def __str__(self) -> str: return \"Magic room\" class OrdinaryRoom(Room): def __str__(self) -> str: return \"Ordinary room\" ordinaryGame = OrdinaryMazeGame() ordinaryGame.play() magicGame = MagicMazeGame() magicGame.play() "
    from abc import ABC , abstractmethodclass MazeGame ( ABC ): def __init__ ( self ) -> None : self . rooms = [] self . _prepare_rooms ()def _prepare_rooms ( self ) - > None : room1 = self.make_room ( ) room2 = self.make_room ( )chambre1.connecter ( chambre2 ) self.chambres.append ( chambre1 ) self.chambres.append ( chambre2 )def play ( self ) -> None : print ( f "Je joue en utilisant { self.rooms [ 0 ] } " )@abstractmethod def make_room ( self ): raise NotImplementedError ( "Vous devriez implémenter ceci !" )classe MagicMazeGame ( MazeGame ): def make_room ( self ) -> "MagicRoom" : return MagicRoom ()classe OrdinaryMazeGame ( MazeGame ): def make_room ( self ) -> "OrdinaryRoom" : return OrdinaryRoom ()classe Salle ( ABC ): def __init__ ( self ) -> None : self . connected_rooms = []def connect ( self , room : " Room " ) - > None : self.connected_rooms.append ( room )classe MagicRoom ( Room ): def __str__ ( self ) -> str : return "Magic room"class OrdinaryRoom ( Room ): def __str__ ( self ) -> str : return "Chambre ordinaire"Jeuordinaire = JeuLabyrintheOrdinaire ( ) Jeuordinaire.jouer ( )magicGame = MagicMazeGame ( ) magicGame.play ( )

    Utilisations