Quels sont les exemples courants et réels d'utilisation du modèle de générateur? Qu'est-ce que ça t'achète? Pourquoi ne pas simplement utiliser un motif d'usine?
java
design-patterns
builder
Charles Graham
la source
la source
Réponses:
La principale différence entre un constructeur et une IMHO d'usine, c'est qu'un constructeur est utile lorsque vous devez faire beaucoup de choses pour construire un objet. Imaginez par exemple un DOM. Vous devez créer de nombreux nœuds et attributs pour obtenir votre objet final. Une fabrique est utilisée lorsque la fabrique peut facilement créer l'objet entier dans un appel de méthode.
Un exemple d'utilisation d'un générateur est la construction d'un document XML, j'ai utilisé ce modèle lors de la création de fragments HTML par exemple, je pourrais avoir un générateur pour construire un type spécifique de table et il pourrait avoir les méthodes suivantes (les paramètres ne sont pas affichés) :
Ce constructeur cracherait alors le HTML pour moi. C'est beaucoup plus facile à lire que de parcourir une grande méthode procédurale.
Découvrez Builder Pattern sur Wikipedia .
la source
Voici quelques raisons plaidant en faveur de l'utilisation du modèle et de l'exemple de code en Java, mais il s'agit d'une implémentation du modèle de générateur couvert par le gang des quatre dans les modèles de conception . Les raisons pour lesquelles vous l'utiliseriez en Java s'appliquent également à d'autres langages de programmation.
Comme l'indique Joshua Bloch dans Effective Java, 2nd Edition :
Nous avons tous à un moment donné rencontré une classe avec une liste de constructeurs où chaque ajout ajoute un nouveau paramètre d'option:
C'est ce qu'on appelle le modèle de constructeur télescopique. Le problème avec ce modèle est qu'une fois que les constructeurs ont 4 ou 5 paramètres, il devient difficile de se souvenir de l' ordre requis des paramètres ainsi que du constructeur particulier que vous pourriez souhaiter dans une situation donnée.
Une alternative que vous avez au modèle de constructeur télescopique est le modèle JavaBean où vous appelez un constructeur avec les paramètres obligatoires, puis appelez tous les paramètres facultatifs après:
Le problème ici est que, parce que l'objet est créé sur plusieurs appels, il peut être dans un état incohérent au cours de sa construction. Cela nécessite également beaucoup d'efforts supplémentaires pour garantir la sécurité du fil.
La meilleure alternative est d'utiliser le modèle Builder.
Notez que Pizza est immuable et que les valeurs des paramètres sont toutes au même endroit . Étant donné que les méthodes de définition du générateur renvoient l'objet générateur, elles peuvent être chaînées .
Il en résulte un code facile à écrire et très facile à lire et à comprendre. Dans cet exemple, la méthode de génération peut être modifiée pour vérifier les paramètres après qu'ils ont été copiés du générateur vers l'objet Pizza et lever une IllegalStateException si une valeur de paramètre non valide a été fournie. Ce modèle est flexible et il est facile d'y ajouter plus de paramètres à l'avenir. Il n'est vraiment utile que si vous allez avoir plus de 4 ou 5 paramètres pour un constructeur. Cela dit, cela pourrait être utile en premier lieu si vous pensez que vous ajouterez peut-être plus de paramètres à l'avenir.
J'ai emprunté massivement à ce sujet dans le livre Effective Java, 2nd Edition de Joshua Bloch. Pour en savoir plus sur ce modèle et d'autres pratiques Java efficaces, je le recommande vivement.
la source
new Pizza.Builder(12).cheese().pepperoni().bacon().build();
Pizza.Builder(12).cheese().pepperoni().bacon().build();
vous auriez besoin de recompiler votre code ou d'avoir une logique inutile si vous n'avez besoin que de pepperoni pizzas. À tout le moins, vous devez également fournir des versions paramétrées comme @Kamikaze Mercenary initialement suggéré.Pizza.Builder(12).cheese(true).pepperoni(false).bacon(false).build();
. Là encore, nous ne faisons jamais de tests unitaires, n'est-ce pas?Considérez un restaurant. La création du «repas d'aujourd'hui» est un modèle d'usine, car vous dites à la cuisine «donnez-moi le repas d'aujourd'hui» et la cuisine (l'usine) décide quel objet générer, en fonction de critères cachés.
Le constructeur apparaît si vous commandez une pizza personnalisée. Dans ce cas, le serveur dit au chef (constructeur) "J'ai besoin d'une pizza; ajoutez-y du fromage, des oignons et du bacon!" Ainsi, le générateur expose les attributs que l'objet généré doit avoir, mais cache comment les définir.
la source
La classe .NET StringBuilder est un excellent exemple de modèle de générateur. Il est principalement utilisé pour créer une chaîne en une série d'étapes. Le résultat final que vous obtenez sur ToString () est toujours une chaîne mais la création de cette chaîne varie en fonction des fonctions de la classe StringBuilder qui ont été utilisées. Pour résumer, l'idée de base est de construire des objets complexes et de masquer les détails d'implémentation de la façon dont il est construit.
la source
b.append(...).append(...)
avant de finalement appelertoString()
. Citation: infoq.com/articles/internal-dsls-javaPour un problème multi-thread, nous avions besoin d'un objet complexe à construire pour chaque thread. L'objet représentait les données en cours de traitement et pouvait changer en fonction de l'entrée utilisateur.
Pourrions-nous utiliser une usine à la place? Oui
Pourquoi pas nous? Le constructeur a plus de sens, je suppose.
Les usines sont utilisées pour créer différents types d'objets qui sont du même type de base (implémenter la même interface ou classe de base).
Les constructeurs construisent le même type d'objet à plusieurs reprises, mais la construction est dynamique et peut donc être modifiée au moment de l'exécution.
la source
Vous l'utilisez lorsque vous avez beaucoup d'options à gérer. Pensez à des choses comme jmock:
Cela semble beaucoup plus naturel et c'est ... possible.
Il y a aussi la construction xml, la construction de chaînes et bien d'autres choses. Imaginez si vous
java.util.Map
aviez mis en tant que constructeur. Vous pouvez faire des trucs comme ça:la source
En passant par le framework Microsoft MVC, j'ai pensé au modèle de générateur. Je suis tombé sur le modèle dans la classe ControllerBuilder. Cette classe doit renvoyer la classe d'usine du contrôleur, qui est ensuite utilisée pour construire le contrôleur concret.
L'avantage que je vois dans l'utilisation du modèle de générateur est que vous pouvez créer votre propre usine et la connecter au framework.
@Tetha, il peut y avoir un restaurant (Framework) géré par un italien, qui sert de la pizza. Afin de préparer une pizza, un gars italien (Object Builder) utilise Owen (Factory) avec une base de pizza (classe de base).
Maintenant, un Indien prend le relais d'un italien. Restaurant indien (Framework) serveurs dosa au lieu de pizza. Afin de préparer dosa Indian guy (constructeur d'objet) utilise Frying Pan (Factory) avec un Maida (classe de base)
Si vous regardez le scénario, la nourriture est différente, la façon dont la nourriture est préparée est différente, mais dans le même restaurant (dans le même cadre). Le restaurant doit être construit de manière à pouvoir prendre en charge la cuisine chinoise, mexicaine ou toute autre cuisine. Le générateur d'objets à l'intérieur du cadre facilite le plugin du type de cuisine que vous souhaitez. par exemple
la source
J'ai toujours détesté le modèle Builder comme quelque chose de lourd, de gênant et très souvent abusé par des programmeurs moins expérimentés. C'est un modèle qui n'a de sens que si vous devez assembler l'objet à partir de certaines données qui nécessitent une étape de post-initialisation (c'est-à-dire une fois que toutes les données sont collectées - faites quelque chose avec). Au lieu de cela, dans 99% du temps, les générateurs sont simplement utilisés pour initialiser les membres de la classe.
Dans de tels cas, il est de loin préférable de simplement déclarer
withXyz(...)
les sélecteurs de type à l'intérieur de la classe et de les renvoyer une référence à lui-même.Considère ceci:
Nous avons maintenant une classe unique soignée qui gère sa propre initialisation et fait à peu près le même travail que le constructeur, sauf que c'est beaucoup plus élégant.
la source
Un autre avantage du constructeur est que si vous avez une Factory, il y a encore du couplage dans votre code, car pour que la Factory fonctionne, elle doit connaître tous les objets qu'elle peut éventuellement créer . Si vous ajoutez un autre objet qui pourrait être créé, vous devrez modifier la classe d'usine pour l'inclure. Cela se produit également dans l'usine abstraite.
Avec le constructeur, en revanche, il vous suffit de créer un nouveau constructeur de béton pour cette nouvelle classe. La classe directrice restera la même, car elle reçoit le constructeur dans le constructeur.
En outre, il existe de nombreuses saveurs de constructeur. Kamikaze Mercenary`s en donne un autre.
la source
la source
S'appuyant sur les réponses précédentes (jeu de mots voulu), un excellent exemple concret est le support intégré de Groovy pour
Builders
.MarkupBuilder
StreamingMarkupBuilder
SwingXBuilder
Voir Builders dans la documentation Groovy
la source
J'ai utilisé Builder dans une bibliothèque de messagerie locale. Le noyau de la bibliothèque recevait des données du fil, les collectait avec l'instance Builder, puis, une fois que Builder avait décidé qu'il avait tout ce dont il avait besoin pour créer une instance de Message, Builder.GetMessage () construisait une instance de message en utilisant les données collectées à partir du câble.
la source
Découvrez InnerBuilder, un plugin IntelliJ IDEA qui ajoute une action «Builder» au menu Générer (Alt + Insert) qui génère une classe de générateur interne comme décrit dans Effective Java
https://github.com/analytically/innerbuilder
la source
Quand j'ai voulu utiliser le XMLGregorianCalendar standard pour mon XML pour le marshaling d'objet de DateTime en Java, j'ai entendu beaucoup de commentaires sur le poids et la lourdeur de son utilisation. J'essayais de contrôler les champs XML dans les structures xs: datetime pour gérer le fuseau horaire, les millisecondes, etc.
J'ai donc conçu un utilitaire pour construire un calendrier XMLGregorian à partir d'un GregorianCalendar ou java.util.Date.
En raison de l'endroit où je travaille, je ne suis pas autorisé à le partager en ligne sans autorisation légale, mais voici un exemple de la façon dont un client l'utilise. Il résume les détails et filtre certaines des implémentations de XMLGregorianCalendar qui sont moins utilisées pour xs: datetime.
Étant donné que ce modèle est plus un filtre car il définit les champs du xmlCalendar comme non définis afin qu'ils soient exclus, il le "construit" toujours. J'ai facilement ajouté d'autres options au générateur pour créer une structure xs: date et xs: time et également pour manipuler les décalages de fuseau horaire en cas de besoin.
Si vous avez déjà vu du code qui crée et utilise XMLGregorianCalendar, vous verriez comment cela a rendu la manipulation beaucoup plus facile.
la source
Un excellent exemple dans le monde réel est à utiliser lors des tests unitaires de vos classes. Vous utilisez des générateurs sut (System Under Test).
Exemple:
Classe:
Tester:
sut Builder:
la source
CustomAuthenticationService
classe?