Quand est-ce une bonne idée d'utiliser des méthodes de fabrique dans un objet au lieu d'une classe de fabrique?
Quand est-ce une bonne idée d'utiliser des méthodes de fabrique dans un objet au lieu d'une classe de fabrique?
Réponses:
J'aime penser aux modèles de conception en termes de mes classes étant des «personnes», et les modèles sont les façons dont les gens se parlent.
Donc, pour moi, le modèle d'usine est comme une agence d'embauche. Vous avez quelqu'un qui aura besoin d'un nombre variable de travailleurs. Cette personne peut connaître les informations dont elle a besoin chez les personnes qu'elle embauche, mais c'est tout.
Ainsi, lorsqu'ils ont besoin d'un nouvel employé, ils appellent l'agence d'embauche et leur disent ce dont ils ont besoin. Maintenant, pour réellement embaucher quelqu'un, vous devez connaître beaucoup de choses - avantages sociaux, vérification de l'admissibilité, etc. Mais la personne qui embauche n'a pas besoin de savoir tout cela - l'agence d'embauche gère tout cela.
De la même manière, l'utilisation d'une usine permet au consommateur de créer de nouveaux objets sans avoir à connaître les détails de la façon dont ils ont été créés, ni quelles sont leurs dépendances - ils n'ont qu'à fournir les informations qu'ils souhaitent réellement.
public interface IThingFactory
{
Thing GetThing(string theString);
}
public class ThingFactory : IThingFactory
{
public Thing GetThing(string theString)
{
return new Thing(theString, firstDependency, secondDependency);
}
}
Ainsi, maintenant, le consommateur de la ThingFactory peut obtenir une chose, sans avoir à connaître les dépendances de la chose, à l'exception des données de chaîne qui proviennent du consommateur.
within an object instead of a Factory class
. Je pense qu'il voulait dire le scénario où vous rendez le ctor privé et utilisez une méthode statique pour instancier la classe (créer un objet). Mais pour suivre cet exemple, il faut d'abord instancier la ThingFactory
classe pour obtenir des Thing
objets, ce qui en fait un Factory class
effet.
Les méthodes d'usine devraient être considérées comme une alternative aux constructeurs - surtout lorsque les constructeurs ne sont pas assez expressifs, c'est-à-dire.
class Foo{
public Foo(bool withBar);
}
n'est pas aussi expressif que:
class Foo{
public static Foo withBar();
public static Foo withoutBar();
}
Les classes d'usine sont utiles lorsque vous avez besoin d'un processus compliqué pour construire l'objet, lorsque la construction a besoin d'une dépendance que vous ne voulez pas pour la classe réelle, lorsque vous devez construire différents objets, etc.
Une situation où je trouve personnellement des classes Factory distinctes pour donner un sens est lorsque l'objet final que vous essayez de créer repose sur plusieurs autres objets. Par exemple, en PHP: Supposons que vous ayez un House
objet, qui à son tour a un Kitchen
et un LivingRoom
objet, et que l' LivingRoom
objet ait également un TV
objet à l'intérieur.
La méthode la plus simple pour y parvenir est que chaque objet crée ses enfants sur sa méthode de construction, mais si les propriétés sont relativement imbriquées, lorsque votre House
création échoue, vous passerez probablement un certain temps à essayer d'isoler exactement ce qui échoue.
L'alternative est de faire ce qui suit (injection de dépendance, si vous aimez le terme de fantaisie):
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
Ici, si le processus de création d'un House
échoue, il n'y a qu'un seul endroit où chercher, mais devoir utiliser ce morceau chaque fois que l'on veut un nouveau House
est loin d'être pratique. Entrez dans les usines:
class HouseFactory {
public function create() {
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
return $HouseObj;
}
}
$houseFactory = new HouseFactory();
$HouseObj = $houseFactory->create();
Grâce à l'usine ici, le processus de création d'un House
est abstrait (en ce que vous n'avez pas besoin de créer et de configurer chaque dépendance unique lorsque vous voulez simplement créer un House
) et en même temps centralisé, ce qui facilite la maintenance. Il y a d'autres raisons pour lesquelles l'utilisation d'usines séparées peut être bénéfique (par exemple la testabilité) mais je trouve ce cas d'utilisation spécifique pour illustrer au mieux comment les classes d'usine peuvent être utiles.
HouseFactory
classe?
create
méthode. Par exemple, si votre House
va toujours avoir le même type, LivingRoom
il peut être judicieux de coder en dur ses paramètres dans la classe d'usine plutôt que de les passer comme arguments. Ou vous pouvez vouloir fournir un type
argument à votre HouseFactory::create
méthode si vous avez quelques types de LivingRoom
s et avez un commutateur à l'intérieur avec les paramètres codés en dur pour chaque type.
Il est important de différencier clairement l'idée derrière l'utilisation de l'usine ou de la méthode d'usine. Les deux sont destinés à résoudre différents types de problèmes de création d'objets mutuellement exclusifs.
Soyons précis sur la "méthode d'usine":
La première chose est que, lorsque vous développez une bibliothèque ou des API qui seront à leur tour utilisées pour le développement d'applications ultérieures, la méthode d'usine est l'une des meilleures sélections pour le modèle de création. Raison derrière; Nous savons que quand créer un objet avec les fonctionnalités requises, mais le type d'objet restera indécis ou il sera décidé que les paramètres dynamiques seront passés .
Maintenant, le fait est qu'environ la même chose peut être obtenue en utilisant le modèle d'usine lui-même, mais un énorme inconvénient introduira dans le système si le modèle d'usine sera utilisé pour le problème mis en évidence ci-dessus, c'est que votre logique de création d'objets différents (objets de sous-classes) être spécifique à certaines conditions commerciales, donc à l'avenir lorsque vous aurez besoin d'étendre les fonctionnalités de votre bibliothèque pour d'autres plates-formes (plus techniquement, vous devez ajouter plus de sous-classes d'interface de base ou de classe abstraite afin que l'usine renvoie également ces objets en plus de ceux existants) sur la base de certains paramètres dynamiques), puis chaque fois que vous devez modifier (étendre) la logique de la classe d'usine, ce qui sera coûteux et pas bon du point de vue de la conception. De l'autre côté, si "méthode d'usine"
interface Deliverable
{
/*********/
}
abstract class DefaultProducer
{
public void taskToBeDone()
{
Deliverable deliverable = factoryMethodPattern();
}
protected abstract Deliverable factoryMethodPattern();
}
class SpecificDeliverable implements Deliverable
{
/***SPECIFIC TASK CAN BE WRITTEN HERE***/
}
class SpecificProducer extends DefaultProducer
{
protected Deliverable factoryMethodPattern()
{
return new SpecificDeliverable();
}
}
public class MasterApplicationProgram
{
public static void main(String arg[])
{
DefaultProducer defaultProducer = new SpecificProducer();
defaultProducer.taskToBeDone();
}
}
C'est une bonne idée d'utiliser des méthodes d'usine à l' intérieur de l'objet lorsque:
C'est une bonne idée d'utiliser la classe d' usine abstraite lorsque:
UML à partir de
Produit: Il définit une interface des objets créés par la méthode Factory.
ConcreteProduct: implémente l'interface produit
Créateur: déclare la méthode Factory
ConcreateCreator: implémente la méthode Factory pour renvoyer une instance d'un ConcreteProduct
Énoncé du problème: créez une usine de jeux à l'aide des méthodes d'usine, qui définissent l'interface de jeu.
Extrait de code:
import java.util.HashMap;
/* Product interface as per UML diagram */
interface Game{
/* createGame is a complex method, which executes a sequence of game steps */
public void createGame();
}
/* ConcreteProduct implementation as per UML diagram */
class Chess implements Game{
public Chess(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Chess game");
System.out.println("Opponents:2");
System.out.println("Define 64 blocks");
System.out.println("Place 16 pieces for White opponent");
System.out.println("Place 16 pieces for Black opponent");
System.out.println("Start Chess game");
System.out.println("---------------------------------------");
}
}
class Checkers implements Game{
public Checkers(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Checkers game");
System.out.println("Opponents:2 or 3 or 4 or 6");
System.out.println("For each opponent, place 10 coins");
System.out.println("Start Checkers game");
System.out.println("---------------------------------------");
}
}
class Ludo implements Game{
public Ludo(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Ludo game");
System.out.println("Opponents:2 or 3 or 4");
System.out.println("For each opponent, place 4 coins");
System.out.println("Create two dices with numbers from 1-6");
System.out.println("Start Ludo game");
System.out.println("---------------------------------------");
}
}
/* Creator interface as per UML diagram */
interface IGameFactory {
public Game getGame(String gameName);
}
/* ConcreteCreator implementation as per UML diagram */
class GameFactory implements IGameFactory {
HashMap<String,Game> games = new HashMap<String,Game>();
/*
Since Game Creation is complex process, we don't want to create game using new operator every time.
Instead we create Game only once and store it in Factory. When client request a specific game,
Game object is returned from Factory instead of creating new Game on the fly, which is time consuming
*/
public GameFactory(){
games.put(Chess.class.getName(),new Chess());
games.put(Checkers.class.getName(),new Checkers());
games.put(Ludo.class.getName(),new Ludo());
}
public Game getGame(String gameName){
return games.get(gameName);
}
}
public class NonStaticFactoryDemo{
public static void main(String args[]){
if ( args.length < 1){
System.out.println("Usage: java FactoryDemo gameName");
return;
}
GameFactory factory = new GameFactory();
Game game = factory.getGame(args[0]);
if ( game != null ){
game.createGame();
System.out.println("Game="+game.getClass().getName());
}else{
System.out.println(args[0]+ " Game does not exists in factory");
}
}
}
production:
java NonStaticFactoryDemo Chess
---------------------------------------
Create Chess game
Opponents:2
Define 64 blocks
Place 16 pieces for White opponent
Place 16 pieces for Black opponent
Start Chess game
---------------------------------------
Game=Chess
Cet exemple montre une Factory
classe en implémentant un FactoryMethod
.
Game
est l'interface pour tous les types de jeux. Il définit une méthode complexe:createGame()
Chess, Ludo, Checkers
sont différentes variantes de jeux, qui permettent la mise en œuvre de createGame()
public Game getGame(String gameName)
est FactoryMethod
en IGameFactory
classe
GameFactory
pré-crée différents types de jeux dans le constructeur. Il implémente la IGameFactory
méthode d'usine.
le nom du jeu est passé comme argument de ligne de commande à NotStaticFactoryDemo
getGame
in GameFactory
accepte un nom de jeu et renvoie l' Game
objet correspondant .
Usine:
Crée des objets sans exposer la logique d'instanciation au client.
FactoryMethod
Définissez une interface pour créer un objet, mais laissez les sous-classes décider de la classe à instancier. La méthode Factory permet à une classe de reporter l'instanciation aux sous-classes
Cas d'utilisation:
Quand l'utiliser: Client
ne sait pas quelles classes concrètes il devra créer au moment de l'exécution, mais veut juste obtenir une classe qui fera le travail.
getArea()
n'est pas une méthode de fabrication du tout .
C'est vraiment une question de goût. Les classes d'usine peuvent être abstraites / interfacées si nécessaire, tandis que les méthodes d'usine sont plus légères (et ont également tendance à être testables, car elles n'ont pas de type défini, mais elles nécessiteront un point d'enregistrement bien connu, semblable à un service localisateur mais pour localiser les méthodes d'usine).
Les classes de fabrique sont utiles lorsque le type d'objet qu'elles renvoient a un constructeur privé, lorsque différentes classes de fabrique définissent des propriétés différentes sur l'objet renvoyé ou lorsqu'un type de fabrique spécifique est couplé avec son type de béton de retour.
WCF utilise les classes ServiceHostFactory pour récupérer des objets ServiceHost dans différentes situations. Le ServiceHostFactory standard est utilisé par IIS pour récupérer des instances ServiceHost pour les fichiers .svc , mais un WebScriptServiceHostFactory est utilisé pour les services qui renvoient des sérialisations aux clients JavaScript. ADO.NET Data Services a son propre DataServiceHostFactory spécial et ASP.NET a son ApplicationServicesHostFactory car ses services ont des constructeurs privés.
Si vous n'avez qu'une seule classe qui consomme l'usine, vous pouvez simplement utiliser une méthode d'usine dans cette classe.
Envisagez un scénario lorsque vous devez concevoir une classe Commande et Client. Pour des raisons de simplicité et d'exigences initiales, vous ne ressentez pas le besoin d'usine pour la classe Order et remplissez votre application avec de nombreuses instructions 'new Order ()'. Les choses fonctionnent bien.
Maintenant, une nouvelle exigence entre en scène: l'objet Order ne peut pas être instancié sans association Client (nouvelle dépendance). Vous avez maintenant les considérations suivantes.
1- Vous créez une surcharge de constructeur qui ne fonctionnera que pour les nouvelles implémentations. (Pas acceptable). 2- Vous modifiez les signatures Order () et modifiez chaque invocation. (Pas une bonne pratique et une vraie douleur).
Au lieu de cela, si vous avez créé une usine pour Order Class, vous n'avez qu'à modifier une ligne de code et vous êtes prêt à partir. Je suggère la classe Factory pour presque toutes les associations d'agrégats. J'espère que cela pourra aider.
si vous souhaitez créer un objet différent en termes d'utilisation. C'est utile.
public class factoryMethodPattern {
static String planName = "COMMERCIALPLAN";
static int units = 3;
public static void main(String args[]) {
GetPlanFactory planFactory = new GetPlanFactory();
Plan p = planFactory.getPlan(planName);
System.out.print("Bill amount for " + planName + " of " + units
+ " units is: ");
p.getRate();
p.calculateBill(units);
}
}
abstract class Plan {
protected double rate;
abstract void getRate();
public void calculateBill(int units) {
System.out.println(units * rate);
}
}
class DomesticPlan extends Plan {
// @override
public void getRate() {
rate = 3.50;
}
}
class CommercialPlan extends Plan {
// @override
public void getRate() {
rate = 7.50;
}
}
class InstitutionalPlan extends Plan {
// @override
public void getRate() {
rate = 5.50;
}
}
class GetPlanFactory {
// use getPlan method to get object of type Plan
public Plan getPlan(String planType) {
if (planType == null) {
return null;
}
if (planType.equalsIgnoreCase("DOMESTICPLAN")) {
return new DomesticPlan();
} else if (planType.equalsIgnoreCase("COMMERCIALPLAN")) {
return new CommercialPlan();
} else if (planType.equalsIgnoreCase("INSTITUTIONALPLAN")) {
return new InstitutionalPlan();
}
return null;
}
}
Toute classe reportant la création d'objet à sa sous-classe pour l'objet avec lequel elle doit travailler peut être considérée comme un exemple de modèle Factory.
J'ai mentionné en détail dans une autre réponse à https://stackoverflow.com/a/49110001/504133
Je pense que cela dépendra du degré de couplage lâche que vous souhaitez apporter à votre code.
La méthode d'usine dissocie très bien les choses, mais la classe d'usine non.
En d'autres termes, il est plus facile de changer les choses si vous utilisez la méthode d'usine que si vous utilisez une usine simple (connue sous le nom de classe d'usine).
Regardez cet exemple: https://connected2know.com/programming/java-factory-pattern/ . Maintenant, imaginez que vous voulez amener un nouvel animal. Dans la classe Factory, vous devez changer Factory, mais dans la méthode Factory, non, vous avez seulement besoin d'ajouter une nouvelle sous-classe.
Les classes d'usine sont plus lourdes, mais vous offrent certains avantages. Dans les cas où vous devez créer vos objets à partir de plusieurs sources de données brutes, ils vous permettent d'encapsuler uniquement la logique de construction (et peut-être l'agrégation des données) en un seul endroit. Là, il peut être testé de manière abstraite sans se préoccuper de l'interface objet.
J'ai trouvé cela un modèle utile, en particulier lorsque je suis incapable de remplacer et d'ORM inadéquat et que je souhaite instancier efficacement de nombreux objets à partir de jointures de table DB ou de procédures stockées.
Je compare les usines au concept de bibliothèques. Par exemple, vous pouvez avoir une bibliothèque pour travailler avec des nombres et une autre pour travailler avec des formes. Vous pouvez stocker les fonctions de ces bibliothèques dans des répertoires logiquement nommés Numbers
ou Shapes
. Ce sont des types génériques qui pourraient inclure des entiers, des flottants, des dobules, des longs ou des rectangles, des cercles, des triangles, des pentagones dans le cas des formes.
Le petter d'usine utilise le polymorphisme, l'injection de dépendance et l'inversion de contrôle.
Le but déclaré des modèles d'usine est: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
Supposons donc que vous construisez un système d'exploitation ou une infrastructure et que vous construisez tous les composants discrets.
Voici un exemple simple du concept du Factory Pattern en PHP. Je ne suis peut-être pas à 100% sur tout cela, mais il est destiné à servir d'exemple simple. Je ne suis pas un expert.
class NumbersFactory {
public static function makeNumber( $type, $number ) {
$numObject = null;
$number = null;
switch( $type ) {
case 'float':
$numObject = new Float( $number );
break;
case 'integer':
$numObject = new Integer( $number );
break;
case 'short':
$numObject = new Short( $number );
break;
case 'double':
$numObject = new Double( $number );
break;
case 'long':
$numObject = new Long( $number );
break;
default:
$numObject = new Integer( $number );
break;
}
return $numObject;
}
}
/* Numbers interface */
abstract class Number {
protected $number;
public function __construct( $number ) {
$this->number = $number;
}
abstract public function add();
abstract public function subtract();
abstract public function multiply();
abstract public function divide();
}
/* Float Implementation */
class Float extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Integer Implementation */
class Integer extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Short Implementation */
class Short extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Double Implementation */
class Double extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Long Implementation */
class Long extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
$number = NumbersFactory::makeNumber( 'float', 12.5 );
NumbersFactory::makeNumber( 'float', 12.5 );
ce qui me donne juste de dire new Float(12.5);
si je sais que j'ai besoin d'un Float
? C'est ce que je ne comprends pas des usines ... à quoi ça sert?