40 votes

Comment améliorer le modèle de générateur?

La Motivation

Récemment, j'ai cherché un moyen d'initialiser un objet complexe sans passer beaucoup de paramètre au constructeur. Je l'ai essayé avec le générateur de modèle, mais je n'aime pas le fait que je ne suis pas en mesure de vérifier au moment de la compilation si j'ai vraiment mis toutes les valeurs nécessaires.

Traditionnelle générateur de modèle

Lorsque j'utilise le générateur de modèle pour créer mon Complex objet, la création est plus "typesafe", parce qu'il est plus facile de voir ce qu'un argument est utilisé pour:

new ComplexBuilder()
        .setFirst( "first" )
        .setSecond( "second" )
        .setThird( "third" )
        ...
        .build();

Mais maintenant j'ai le problème, que je peux facilement passer à côté d'un paramètre important. Je peux vérifier à l'intérieur de l' build() méthode, mais c'est seulement au moment de l'exécution. Au moment de la compilation il n'y a rien qui me prévient, si j'ai raté quelque chose.

Amélioré le générateur de modèle

Maintenant, mon idée était de créer un constructeur, qui "rappelle" moi, si j'ai raté un des paramètres nécessaires. Mon premier essai ressemble à ceci:

public class Complexe {
 private String m_first;
 private String m_second;
 private String m_third;

 Complexe privé() {}

 public static de la classe ComplexBuilder {
 Complexe privé m_complex;

 public ComplexBuilder() {
 m_complex = new Complexe();
}

 public Builder2 setFirst( la Chaîne ) {
 m_complex.m_first = premier;
 de retour de nouvelle Builder2();
}

 public class Builder2 {
 privé Builder2() {}
 Builder3 setSecond( deuxième Chaîne ) {
 m_complex.m_second = deuxième;
 de retour de nouvelle Builder3();
}
}

 public class Builder3 {
 privé Builder3() {}
 Builder4 setThird( Chaîne de troisième ) {
 m_complex.m_third = tiers;
 de retour de nouvelle Builder4();
}
}

 public class Builder4 {
 privé Builder4() {}
 Complexe build() {
 retour m_complex;
}
}
}
}

Comme vous pouvez le voir, chaque poseur de le constructeur de la classe retourne différentes interne constructeur de la classe. Interne de chaque constructeur de la classe fournit exactement une méthode de définition et le dernier ne fournit qu'une méthode build ().

Maintenant, la construction d'un objet nouveau ressemble à ceci:

new ComplexBuilder()
    .setFirst( "first" )
    .setSecond( "second" )
    .setThird( "third" )
    .build();

...mais il n'y a pas moyen d'oublier un des paramètres nécessaires. Le compilateur n'accepte pas.

Les paramètres facultatifs

Si j'avais des paramètres facultatifs, je voudrais utiliser la dernière interne constructeur de la classe Builder4 pour les définir comme "traditionnelles" générateur de fait, le retour lui-même.

Questions

  • Est-ce un bien connus modèle? A-t-elle un nom particulier?
  • Voyez-vous des inconvénients?
  • Avez-vous des idées pour améliorer la mise en œuvre - dans le sens de moins de lignes de code?

23voto

Michael Borgwardt Points 181658

Le modèle de générateur traditionnel gère déjà cela: prenez simplement les paramètres obligatoires dans le constructeur. Bien sûr, rien n'empêche un appelant de passer null, mais votre méthode non plus.

Le gros problème que je vois avec votre méthode est que vous avez soit une explosion combinatoire de classes avec le nombre de paramètres obligatoires, soit forcez l'utilisateur à définir les paramètres dans une séquence particulière, ce qui est ennuyeux.

C'est aussi beaucoup de travail supplémentaire.

16voto

Esko Points 15578

Non, ce n'est pas nouveau. Ce que vous êtes réellement en train de faire là est la création d'une sorte de DSL par l'extension de la norme générateur de motif à l'appui de branches qui est entre autres choses une excellente façon de s'assurer que le constructeur n'est pas de produire un ensemble de paramètres contradictoires de l'objet réel.

Personnellement, je pense que c'est une grande extension de générateur de modèle et vous pouvez faire toutes sortes de choses intéressantes avec, par exemple, au travail, nous avons DSL constructeurs pour certains de nos intégrité des données de tests qui nous permettent de faire des choses comme assertMachine().usesElectricity().and().makesGrindingNoises().whenTurnedOn();. OK, peut-être pas le meilleur exemple possible mais je pense que vous obtenez le point.

13voto

clinux Points 591
public class Complex {
    private final String first;
    private final String second;
    private final String third;

    public static class False {}
    public static class True {}

    public static class Builder<Has1,Has2,Has3> {
        private String first;
        private String second;
        private String third;

        private Builder() {}

        public static Builder<False,False,False> create() {
            return new Builder<>();
        }

        public Builder<True,Has2,Has3> setFirst(String first) {
            this.first = first;
            return (Builder<True,Has2,Has3>)this;
        }

        public Builder<Has1,True,Has3> setSecond(String second) {
            this.second = second;
            return (Builder<Has1,True,Has3>)this;
        }

        public Builder<Has1,Has2,True> setThird(String third) {
            this.third = third;
            return (Builder<Has1,Has2,True>)this;
        }
    }

    public Complex(Builder<True,True,True> builder) {
        first = builder.first;
        second = builder.second;
        third = builder.third;
    }

    public static void test() {
        // Compile Error!
        Complex c1 = new Complex(Complex.Builder.create().setFirst("1").setSecond("2"));

        // Compile Error!
        Complex c2 = new Complex(Complex.Builder.create().setFirst("1").setThird("3"));

        // Works!, all params supplied.
        Complex c3 = new Complex(Complex.Builder.create().setFirst("1").setSecond("2").setThird("3"));
    }
}

12voto

Strawberry Points 3323

Pourquoi ne mettez-vous pas les paramètres "nécessaires" dans le constructeur des constructeurs?

 public class Complex
{
....
  public static class ComplexBuilder
  {
     // Required parameters
     private final int required;

     // Optional parameters
     private int optional = 0;

     public ComplexBuilder( int required )
     {
        this.required = required;
     } 

     public Builder setOptional(int optional)
     {
        this.optional = optional;
     }
  }
...
}
 

Ce modèle est décrit dans Java efficace .

5voto

JRL Points 36674

À mon humble avis, cela semble gonflé. Si vous devez avoir tous les paramètres, passez-les dans le constructeur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X