Lors de la modélisation de classes, quelle est la méthode d'initialisation préférée:
- Constructeurs, ou
- Méthodes d'usine
Et quelles seraient les considérations pour utiliser l'un ou l'autre?
Dans certaines situations, je préfère avoir une méthode de fabrique qui retourne null si l'objet ne peut pas être construit. Cela rend le code soigné. Je peux simplement vérifier si la valeur retournée n'est pas nulle avant de prendre une action alternative, contrairement à la levée d'une exception du constructeur. (Personnellement, je n'aime pas les exceptions)
Dites, j'ai un constructeur sur une classe qui attend une valeur id. Le constructeur utilise cette valeur pour remplir la classe à partir de la base de données. Dans le cas où un enregistrement avec l'ID spécifié n'existe pas, le constructeur lève une RecordNotFoundException. Dans ce cas, je devrai inclure la construction de toutes ces classes dans un bloc try..catch.
Contrairement à cela, je peux avoir une méthode de fabrique statique sur ces classes qui renverra null si l'enregistrement n'est pas trouvé.
Quelle approche est la meilleure dans ce cas, méthode constructeur ou méthode d'usine?
Demandez-vous ce qu'ils sont et pourquoi nous les avons. Ils sont tous deux là pour créer une instance d'un objet.
Aucune différence jusqu'à présent. Imaginez maintenant que nous avons différents types d'écoles et que nous souhaitons passer de ElementarySchool à HighSchool (qui est dérivé d'une ElementarySchool ou implémente la même interface ISchool que ElementarySchool). Le changement de code serait:
Dans le cas d'une interface, nous aurions:
Maintenant, si vous avez ce code à plusieurs endroits, vous pouvez voir que l'utilisation de la méthode d'usine peut être assez bon marché car une fois que vous avez changé la méthode d'usine, vous avez terminé (si nous utilisons le deuxième exemple avec des interfaces).
Et c'est la principale différence et avantage. Lorsque vous commencez à traiter des hiérarchies de classes complexes et que vous souhaitez créer dynamiquement une instance d'une classe à partir d'une telle hiérarchie, vous obtenez le code suivant. Les méthodes d'usine peuvent alors prendre un paramètre qui indique à la méthode quelle instance concrète instancier. Disons que vous avez une classe MyStudent et que vous devez instancier l'objet ISchool correspondant afin que votre élève soit membre de cette école.
Vous disposez désormais d'un emplacement dans votre application qui contient la logique métier qui détermine l'objet ISchool à instancier pour différents objets IStudent.
Donc - pour les classes simples (objets de valeur, etc.), le constructeur est très bien (vous ne voulez pas surenginer votre application) mais pour les hiérarchies de classes complexes, la méthode factory est un moyen préféré.
De cette façon, vous suivez le premier principe de conception du livre de la bande de quatre "Programme vers une interface, pas une implémentation".
la source
IFood sandwich = new Sandwich(Cheese chz, Meat meat);
etIFood soup = new Soup(Broth broth, Vegetable veg);
comment l'usine et / ou le constructeur peuvent-ils aider ici?Vous devez lire (si vous avez accès à) Java 2 efficace. Élément 1: Considérez les méthodes de fabrique statiques au lieu des constructeurs .
Avantages des méthodes statiques d'usine:
Inconvénients des méthodes d'usine statiques:
la source
( factory methods are better than Constructors. ( Item-1 ) ) Effective java
Par défaut, les constructeurs doivent être préférés, car ils sont plus simples à comprendre et à écrire. Cependant, si vous avez spécifiquement besoin de découpler les subtilités de construction d'un objet de sa signification sémantique telle que comprise par le code client, vous feriez mieux d'utiliser des usines.
La différence entre les constructeurs et les usines est analogue, par exemple, à une variable et à un pointeur vers une variable. Il existe un autre niveau d'indirection, ce qui est un inconvénient; mais il y a aussi un autre niveau de flexibilité, ce qui est un avantage. Ainsi, tout en faisant un choix, vous feriez bien de faire cette analyse des coûts par rapport aux avantages.
la source
Utilisez une fabrique uniquement lorsque vous avez besoin d'un contrôle supplémentaire avec la création d'objets, d'une manière qui ne pourrait pas être réalisée avec des constructeurs.
Les usines ont la possibilité de mettre en cache par exemple.
Une autre façon d'utiliser les usines est dans un scénario où vous ne connaissez pas le type que vous souhaitez construire. Souvent, vous voyez ce type d'utilisation dans les scénarios d'usine de plugins, où chaque plugin doit dériver d'une classe de base ou implémenter une sorte d'interface. La fabrique crée des instances de classes qui dérivent de la classe de base ou qui implémentent l'interface.
la source
Une citation de "Effective Java", 2e éd., Point 1: Considérer les méthodes de fabrique statiques au lieu des constructeurs, p. 5:
"Notez qu'une méthode de fabrique statique n'est pas la même que le modèle de méthode d'usine de Design Patterns [Gamma95, p. 107]. La méthode de fabrique statique décrite dans cet article n'a pas d'équivalent direct dans les modèles de conception."
la source
En plus de "java efficace" (comme mentionné dans une autre réponse), un autre livre classique suggère également:
Par exemple. n'écris pas
mais écris plutôt
Le livre va jusqu'à suggérer de rendre le
Complex(float)
constructeur privé, pour forcer l'utilisateur à appeler la méthode de fabrique statique.la source
from…
,to…
,parse…
,with…
et ainsi de suite. Gardez à l'esprit que les classes java.time sont construites pour être immuables, mais certaines de ces conventions de dénomination peuvent également être utiles pour les classes mutables.Un exemple concret d'une application CAO / FAO.
Un chemin de coupe serait créé à l'aide d'un constructeur. C'est une série de lignes et d'arcs définissant un chemin à couper. Bien que la série de lignes et d'arcs puisse être différente et avoir des coordonnées différentes, elle est facilement gérée en passant une liste dans un constructeur.
Une forme serait faite en utilisant une usine. Parce qu'il y a une classe de forme, chaque forme serait configurée différemment en fonction du type de forme. Nous ne savons pas quelle forme nous allons initialiser jusqu'à ce que l'utilisateur fasse une sélection.
la source
Ce processus doit certainement être en dehors d'un constructeur.
Le constructeur ne doit pas accéder à la base de données.
La tâche et la raison d'un constructeur est d' initialiser les données membres et d' établir un invariant de classe en utilisant des valeurs transmises au constructeur.
Pour tout le reste, une meilleure approche consiste à utiliser une méthode de fabrique statique ou, dans des cas plus complexes, une classe de fabrique ou de constructeur distincte .
Quelques lignes directrices du constructeur de Microsoft :
Et
la source
Parfois, vous devez vérifier / calculer certaines valeurs / conditions lors de la création d'un objet. Et s'il peut lancer une exception, la construction est très mauvaise. Vous devez donc faire quelque chose comme ceci:
Où tous les calculs supplémentaires sont dans init (). Mais seul vous, en tant que développeur, connaissez vraiment ce init (). Et bien sûr, après des mois, vous l'oubliez. Mais si vous avez une usine - faites tout ce dont vous avez besoin dans une seule méthode en masquant cet init () à l'appel direct - donc pas de problème. Avec cette approche, il n'y a aucun problème avec la création et la fuite de mémoire.
Quelqu'un vous a parlé de la mise en cache. C'est bon. Mais vous devez également vous souvenir du modèle Flyweight qui est agréable à utiliser avec Factory way.
la source