Modèle de constructeur vs objet de configuration

Le modèle builder est populaire pour créer des objets immuables, mais il y a une surcharge de programmation pour créer un builder. Donc, je me demande pourquoi ne pas simplement utiliser un objet config.

L'utilisation d'un constructeur ressemblerait à ceci:

Product p = Product.Builder.name("Vodka").alcohol(0.38).size(0.7).price(17.99).build();

Il est évident que c'est très lisible et concis, mais vous devez implémenter le builder:

public class Product {

    public final String name;
    public final float alcohol;
    public final float size;
    public final float price;

    private Product(Builder builder) {
        this.name = builder.name;
        this.alcohol = builder.alcohol;
        this.size = builder.size;
        this.price = builder.price;
    }

    public static class Builder {

        private String name;
        private float alcohol;
        private float size;
        private float price;

        // mandatory
        public static Builder name(String name) {
            Builder b = new Builder();
            b.name = name;
            return b;
        }

        public Builder alcohol(float alcohol) {
            this.alcohol = alcohol;
            return.this;
        }

        public Builder size(float size) {
            this.size = size;
            return.this;
        }

        public Builder price(float price) {
            this.price = price;
            return.this;
        }

        public Product build() {
            return new Product(this);
        }

    }

}

Mon idée est de réduire le code en utilisant un objet de configuration simple comme ceci:

class ProductConfig {

        public String name;
        public float alcohol;
        public float size;
        public float price;

        // name is still mandatory
        public ProductConfig(String name) {
            this.name = name;
        }

}

public class Product {

    public final String name;
    public final float alcohol;
    public final float size;
    public final float price;

    public Product(ProductConfig config) {
        this.name = config.name;
        this.alcohol = config.alcohol;
        this.size = config.size;
        this.price = config.price;
    }

}

Utilisation:

ProductConfig config = new ProductConfig("Vodka");
config.alcohol = 0.38;
config.size = 0.7;
config.price = 17.99;
Product p = new Product(config);

Cette Utilisation a besoin quelques lignes de plus mais est également très lisible, mais l'implémentation est beaucoup plus simple et peut-être plus facile à comprendre pour quelqu'un qui ne connaît pas le modèle builder. En passant: est-il un nom pour ce modèle?

Y a-t-il un inconvénient dans l'approche de configuration que j'ai négligée?

28
demandé sur d0nut 2010-08-03 12:30:45

9 réponses

Le modèle builder améliore le découplage-votre produit peut être une interface et la seule classe qui connaît l'implémentation (ou les implémentations, dans certains cas) est le builder. Si le constructeur implémente également une interface, vous pouvez l'injecter dans votre code pour augmenter le découplage.

Ce découplage signifie que votre code est plus maintenable et plus facile à tester.

16
répondu Martin Hutchinson 2010-08-03 08:35:56

Vous perdez plusieurs avantages du modèle builder, comme cela a déjà été souligné ( new est laid et plus difficile à maintenir et fuit des détails par rapport à un builder propre).

Celui qui me manque le plus est que le modèle builder peut être utilisé pour fournir ce qu'on appelle "interfaces fluides" .

Au lieu de ceci:

ProductConfig config = new ProductConfig("Vodka");
config.alcohol = 0.38;
config.size = 0.7;
config.price = 17.99;
Product p = new Product(config);

Vous pouvez faire:

ProductFactory.create()
    .drink("Vodka")
    .whereAlcohoolLevelIs(0.38)
    .inABottleSized(0.7)
    .pricedAt(17.99)
    .build();

Tout le monde n'aime pas les interfaces fluides, mais ils sont certainement une très bonne utilisation de la modèle builder (toutes les interfaces fluent doivent utiliser le modèle builder, mais tous les modèles builder ne sont pas des interfaces fluent).

Certaines grandes collections Java, comme les collections Google, Font à la fois un usage très libéral et très bon de "interfaces fluides" . Je les choisirais tous les jours sur votre"caractères plus faciles à taper/moins" approche:)

6
répondu NoozNooz42 2010-08-03 09:34:48

Quel problème essayez-vous de résoudre avec votre patron? Le modèle builder est utilisé pour les objets avec de nombreux paramètres (optionnels) afin d'éviter des tonnes de constructeurs différents ou de très longs. Il maintient également votre objet dans un état cohérent (par rapport au modèle javabean) pendant la construction.

La différence entre builder et "config object" (se sent comme un bon nom) est que vous devez toujours créer l'objet avec les mêmes paramètres par constructeur ou getter/setter. Cette a) ne marche pas résoudre le problème du constructeur ou B) maintient l'objet config dans un état incohérent. Les États Incohérents de l'objet de configuration ne le blessent pas vraiment mais vous pouvez passer un objet de configuration inachevé en tant que paramètre. [Michids link to phantom types semblent résoudre ce problème, mais cela tue à nouveau la lisibilité (new Foo<TRUE,TRUE, TRUE, FALSE, TRUE> un peu nul).] C'est le gros avantage du modèle builder: vous pouvez valider vos paramètres avant de créer un objet et vous pouvez renvoyer n'importe quel sous-type (agit comme un usine).

Les objets de configuration sont valides pour les ensembles de paramètres qui sont tous obligatoires. J'ai déjà vu ce modèle plusieurs fois dans. NET ou java.

3
répondu atamanroman 2010-08-03 09:10:48

IMO, le modèle builder est beaucoup plus roboust si vous avez des choses comme la validation,etc.

Le modèle builder dans votre cas peut être modifié pour effectuer les opérations suivantes:

Product p = new ProductBuilder("pName").alcohol(0.38).size(0.7).price(17.99).build();

La méthode build() peut faire toutes les choses de validation nécessaires pour votre constructeur. Le modèle builder a également plusieurs avantages de conception (qui peuvent tous ne pas être applicables dans votre cas). Pour deatils vérifier cette question

2
répondu naikus 2017-05-23 12:16:31

Avez-vous considéré l'utilisation de générateur-générateur de?

Je pense que le constructeur (avec des préfixes comme "With") lit plus natrually / couramment.

2
répondu Michael Lloyd Lee mlk 2010-08-03 09:16:43

Personnellement, je pense que le modèle builder à première vue vous offre un code plus propre où ces objets sont en fait utilisés. D'un autre côté, ne pas avoir de getters / setters ne sera pas très utilisable par beaucoup de frameworks qui attendent des getters/setters de camel case. C'est un sérieux recul que je ressens.

Ce que j'aime aussi avec des getters/setters, c'est que vous voyez clairement ce que vous faites: get ou set. Je sens avec le constructeur que je perds un peu de clarté intuitive ici.

Je sais que beaucoup les gens ont lu un livre spécifique et que maintenant tout d'un coup, le modèle de constructeur a apprécié le battage médiatique, comme s'il s'agissait du nouvel iPhone. Cependant, je ne suis pas un adopteur précoce. Je n'utilise "the new way" que lorsque cela prouve vraiment un gros gain de temps sur n'importe quel territoire, que ce soit la performance, la maintenance, le codage...

Mon expérience pratique est que je suis généralement meilleur avec les getters / setters et les constructeurs. Cela me permet de réutiliser ces POJO pour n'importe quel but.

Bien que je vois le but de votre objet de configuration, je pense aussi que c'est encore plus de frais généraux que le constructeur, et pour quoi? Quel est le problème avec setters?

Peut-être que nous devons inventer une clause WITH: exemple, disons que vous avez

public Class FooBar() {
    private String foo;

    public void setFoo(String bar) { 
      this.foo = bar; 
    }

    public String getFoo() { 
        return this.foo; 
    }
}

public static void main(String []args) {

 FooBar fuBar = new FooBar();
 String myBar;

 with fuBar {
    setFoo("bar");
    myBar = getFoo(); 
 }
}

Ah je sais pas... Je pense que cela peut encore entraîner une écriture de code plus rapide sans tous les tracas de classe interne. Est-ce que quelqu'un a des connexions avec le gourou Oracle Java?

Il n'a pas l'air aussi propre que d'utiliser un objet avec un constructeur, mais vous économisez du temps de construction du constructeur. Et vous pouvez toujours utiliser la classe comme un pojo/bean régulier qui peut être utilisé dans les frameworks...

Voulez-vous vraiment cette clause ou vous pensez qu'elle serait plutôt nulle? Cheers

1
répondu Lawrence 2012-09-18 14:18:33

Le modèle de configuration et le modèle de générateur sont fonctionnellement équivalents. Ils résolvent tous les deux les mêmes problèmes -

  • Éliminer le besoin de plusieurs signatures de constructeur

  • Autoriser les champs à être définis uniquement pendant la construction

  • Autoriser les consommateurs à définir uniquement les valeurs qui leur tiennent à cœur et à avoir des valeurs par défaut logiques pour les autres valeurs

Tout ce que vous voulez faire dans l'un de ces modèles, vous pouvez le faire dans l'autre, comme seulement autoriser l'État à être défini avec des méthodes qui font la validation et définir l'état avec une logique encapsulée. La seule vraie différence est si vous aimez créer des objets avec le terme clé new ou si vous aimez appeler une méthode .build().

1
répondu TimCrowe 2017-08-16 22:55:18

Le principal inconvénient est que ce n'est pas dans le livre de Joshua, donc les drones ne peuvent pas envelopper leur tête autour d'elle.

Vous utilisez un objet de valeur simple pour contenir plusieurs arguments dont une fonction(/method/constructor) a besoin, il n'y a rien de mal à cela, cela a été fait depuis longtemps. Tant que nous n'avons pas de paramètres optionnels nommés, nous devrons concevoir des solutions de contournement comme celle - ci-c'est dommage, pas des inventions brillantes des dieux du Soleil.

La vraie différence est que vous exposer les champs directement. Joshua n'aurait jamais un champ mutable public - mais il écrit des API qui seront utilisées par des millions de personnes dont la plupart sont des crétins , et les API doivent être sûres pour évoluer pendant des décennies à venir, et ils peuvent allouer de nombreux mois d'homme juste pour concevoir une classe simple

Qui sommes-nous pour emmuler cela?

0
répondu irreputable 2010-08-03 17:24:37

Vous ne devez pas utiliser le champ public, mais protégé ou privé. Pour l'accès, vous devez utiliser les getters et setter pour conserver l'encapsulation..

-1
répondu Damian Leszczyński - Vash 2010-08-03 08:38:53