Le modèle d'usine (ou du moins l'utilisation de FactoryFactory..
) est le but de nombreuses blagues, comme ici .
En plus d'avoir des noms verbeux et "créatifs" comme RequestProcessorFactoryFactory.RequestProcessorFactory , y a-t-il quelque chose de fondamentalement incorrect avec le modèle d'usine si vous devez programmer en Java / C ++ et qu'il existe un cas d'utilisation pour Abstract_factory_pattern ?
Comment une autre langue populaire (par exemple, Ruby ou Scala ) éviterait-elle d'avoir à l'utiliser tout en gérant une complexité similaire?
La raison pour laquelle je demande, c'est que je vois principalement les critiques des usines mentionnées dans le contexte de l' écosystème Java / Java EE , mais elles n'expliquent jamais comment d'autres langages / cadres les résolvent.
la source
Réponses:
Votre question est étiquetée avec "Java", pas étonnant que vous vous demandez pourquoi le modèle Factory est moqué: Java lui-même est livré avec des abus bien emballés de ce modèle.
Par exemple, essayez de charger un document XML à partir d'un fichier et exécutez une requête XPath sur celui-ci. Vous avez besoin de quelque chose comme 10 lignes de code juste pour configurer les usines et les constructeurs:
Je me demande si les gars qui ont conçu cette API ont déjà travaillé en tant que développeurs ou ont-ils simplement lu des livres et jeté des choses. Je comprends qu'ils ne voulaient tout simplement pas écrire un analyseur eux-mêmes et ont laissé la tâche à d'autres, mais cela rend toujours une implémentation laide.
Puisque vous demandez quelle est l'alternative, voici le chargement du fichier XML en C #:
Je soupçonne que les développeurs Java nouvellement éclos voient la folie Factory et pensent que c'est OK pour le faire - si les génies qui ont eux-mêmes construit Java les ont beaucoup utilisés.
L'usine et tous les autres modèles sont des outils, chacun adapté à un travail particulier. Si vous les appliquez à des emplois auxquels ils ne conviennent pas, vous aurez forcément du code laid.
la source
XmlDocument xml = new XmlDocument(); xml.Load("c:\\employees.xml"); XmlNodeList nodes = xml.SelectNodes(expression);
(Dans l'ensemble, LINQ to XML est beaucoup plus facile à utiliser, mais surtout dans d'autres domaines.) Et voici un article de la base de connaissances J'ai trouvé, avec une méthode recommandée par MS: support.microsoft.com/kb/308333Comme souvent, les gens comprennent mal ce qui se passe (et cela inclut de nombreux rires).
Ce n'est pas le modèle d'usine en soi qui est mauvais autant que la façon dont beaucoup (peut-être même la plupart) les utilisent.
Et cela découle sans aucun doute de la façon dont la programmation (et les modèles) sont enseignés. Les écoliers (souvent appelés eux-mêmes «étudiants») se font dire de «créer X en utilisant le modèle Y» et après quelques itérations de cela pense que c'est la façon d'aborder tout problème de programmation.
Ils commencent donc à appliquer un modèle spécifique qu'ils aiment à l'école contre tout et n'importe quoi, que ce soit approprié ou non.
Et cela inclut malheureusement les professeurs d'université qui écrivent des livres sur la conception de logiciels.
Le point culminant de ceci était un système que j'avais le mécontentement distinct d'avoir à maintenir qui avait été créé par un tel (l'homme avait même plusieurs livres sur la conception orientée objet à son nom, et un poste d'enseignant dans le département CS d'une grande université ).
Il était basé sur un modèle à 3 niveaux, chaque niveau lui-même étant un système à 3 niveaux (à découpler ...). Sur l'interface entre chaque ensemble de niveaux, des deux côtés, il y avait une usine pour produire les objets pour transférer des données à l'autre niveau, et une usine pour produire l'objet pour traduire l'objet reçu en un objet appartenant à la couche réceptrice.
Pour chaque usine, il y avait une usine abstraite (qui sait, l'usine pourrait devoir changer et vous ne voulez pas que le code d'appel doive changer ...).
Et tout ce gâchis était bien sûr complètement sans papiers.
Le système avait une base de données normalisée à la 5ème forme normale (je ne vous trompe pas).
Un système qui n'était pratiquement rien de plus qu'un carnet d'adresses pouvant être utilisé pour consigner et suivre les documents entrants et sortants, il avait une base de code de 100 Mo en C ++ et plus de 50 tables de base de données. L'impression de 500 lettres types prendrait jusqu'à 72 heures avec une imprimante connectée directement à l'ordinateur exécutant le logiciel.
C'EST POURQUOI les gens se moquent des modèles, et en particulier des gens qui se concentrent sur un seul modèle spécifique.
la source
Les usines présentent de nombreux avantages qui permettent de concevoir des applications élégantes dans certaines situations. La première est que vous pouvez définir les propriétés des objets que vous souhaitez créer ultérieurement en un seul endroit en créant une usine, puis remettre cette usine. Mais souvent, vous n'avez pas vraiment besoin de le faire. Dans ce cas, l'utilisation d'une usine ajoute simplement une complexité supplémentaire sans vous donner quoi que ce soit en retour. Prenons cette usine, par exemple:
Une alternative au modèle Factory est le modèle Builder très similaire. La principale différence est que les propriétés des objets créés par une fabrique sont définies lorsque la fabrique est initialisée, tandis qu'un générateur est initialisé avec un état par défaut et toutes les propriétés sont définies par la suite.
Mais lorsque la suringénierie est votre problème, le remplacement d'une usine par un constructeur n'est probablement pas une grande amélioration.
Le remplacement le plus simple de l'un ou l'autre modèle est bien sûr de créer des instances d'objet avec un constructeur simple avec l'
new
opérateur:Les constructeurs, cependant, ont un inconvénient crucial dans la plupart des langages orientés objet: ils doivent renvoyer un objet de cette classe exacte et ne peuvent pas retourner un sous-type.
Lorsque vous devez choisir le sous-type lors de l'exécution mais que vous ne souhaitez pas recourir à la création d'une nouvelle classe Builder ou Factory pour cela, vous pouvez utiliser une méthode d'usine à la place. Il s'agit d'une méthode statique d'une classe qui renvoie une nouvelle instance de cette classe ou l'une de ses sous-classes. Une usine qui ne maintient aucun état interne peut souvent être remplacée par une telle méthode d'usine:
Une nouvelle fonctionnalité de Java 8 sont les références de méthodes qui vous permettent de transmettre des méthodes, comme vous le feriez avec une fabrique sans état. Idéalement, tout ce qui accepte une référence de méthode accepte également tout objet qui implémente la même interface fonctionnelle, qui peut également être une usine à part entière avec un état interne, de sorte que vous pouvez facilement introduire des usines plus tard, lorsque vous voyez une raison de le faire.
la source
foo = new Bar(23);
soit équivalent àfoo = Bar._createInstance(23);
. Un objet doit pouvoir interroger de manière fiable son propre type à l'aide d'unegetRealType()
méthode privée , mais les objets doivent pouvoir désigner un supertype à renvoyer par des appels externes àgetType()
. Le code extérieur ne devrait pas se soucier de savoir si un appel ànew String("A")
renvoie réellement une instance deString
[par opposition à par exemple une instance deSingleCharacterString
].draw(OutputStream out)
mais génèrent du HTML légèrement différent, la fabrique de méthodes statiques crée la bonne classe pour la situation, puis l'appelant peut simplement utiliser la méthode draw.[[SomeClass alloc] init]
. Il est possible de renvoyer une instance de classe complètement différente ou un autre objet (comme une valeur mise en cache)Les usines d'une sorte ou d'une autre se trouvent dans à peu près n'importe quel langage orienté objet dans des circonstances appropriées. Parfois, vous avez simplement besoin d'un moyen de sélectionner le type d'objet à créer en fonction d'un paramètre simple comme une chaîne.
Certaines personnes vont trop loin et essaient d'architecturer leur code pour ne jamais avoir besoin d'appeler un constructeur sauf à l'intérieur d'une usine. Les choses commencent à devenir ridicules lorsque vous avez des usines.
Ce qui m'a frappé quand j'ai appris Scala, et je ne connais pas Ruby, mais je pense que c'est à peu près la même chose, c'est que le langage est suffisamment expressif pour que les programmeurs n'essaient pas toujours de pousser le travail de "plomberie" vers des fichiers de configuration externes . Plutôt que d'être tenté de créer des usines pour relier vos classes dans différentes configurations, dans Scala, vous utilisez des objets avec des traits mélangés. Il est également relativement facile de créer de simples DSL en langage pour des tâches qui sont souvent bien trop sur-conçues en Java.
De plus, comme d'autres commentaires et réponses l'ont souligné, les fermetures et les fonctions de première classe évitent la nécessité de nombreux modèles. Pour cette raison, je pense que de nombreux anti-patterns commenceront à disparaître à mesure que C ++ 11 et Java 8 seront généralisés.
la source
En général, il y a cette tendance que les programmes Java sont horriblement sur-conçus [citation nécessaire]. La présence de nombreuses usines est l'un des symptômes les plus courants de la suringénierie; c'est pourquoi les gens s'en moquent.
En particulier, le problème que Java a avec les usines est qu'en Java a) les constructeurs ne sont pas des fonctions et b) les fonctions ne sont pas des citoyens de première classe.
Imaginez que vous pourriez écrire quelque chose comme ça (
Function
soit une interface bien connue du JRE )Voir, pas d'interfaces ou de classes d'usine. De nombreux langages dynamiques ont des fonctions de première classe. Hélas, cela n'est pas possible avec Java 7 ou une version antérieure (la mise en œuvre de la même chose avec les usines est laissée au lecteur comme exercice).
Ainsi, l'alternative n'est pas tant «d'utiliser un modèle différent», mais plutôt «d'utiliser un langage avec un meilleur modèle d'objet» ou de «ne pas sur-concevoir de simples problèmes».
la source