96 votes

Construire de gros objets immuables sans utiliser de constructeurs ayant de longues listes de paramètres

J'ai des grosses (plus de 3 champs) des Objets qui peuvent et doivent être immuable. Chaque fois que je lance en ce cas, j'ai tendance à créer constructeur abominations avec de longues listes de paramètres. Il ne se sent pas le droit, est difficile à utiliser et la lisibilité souffre.

C'est encore pire si les champs sont en quelque sorte de type de collection comme des listes. Un simple addSibling(S s) aurait facilité la création d'un objet tellement, mais rend l'objet mutable.

Que faites-vous les gars utilisent dans de tels cas? Je suis sur Scala et Java, mais je pense que le problème est la langue agnostique que le langage est orienté objet.

Les Solutions que je pense:

  1. "Constructeur abominations avec de longues listes de paramètres"
  2. Le Générateur De Modèle

Merci pour vos commentaires!

76voto

SyntaxT3rr0r Points 10771

Eh bien, vous voulez à la fois plus simple à lire et immuable de l'objet une fois créé?

Je pense que d'une interface fluide CORRECTEMENT FAIT pourrait vous aider.

Il devrait ressembler à ceci (purement composé de l'exemple):

final Foo immutable = FooFactory.create()
    .whereRangeConstraintsAre(100,300)
    .withColor(Color.BLUE)
    .withArea(234)
    .withInterspacing(12)
    .build();

J'ai écrit "bien FAIT" en gras parce que la plupart des programmeurs Java te mettre à l'aise les interfaces mal et polluer leur objet avec la méthode nécessaires à la construction de l'objet, qui est bien sûr complètement faux.

Le truc, c'est que seule la méthode build() crée un Foo (d'où vous Foo peut être immuable).

FooFactory.créer(), whereXXX(..) et withXXX(..) tous créer "autre chose".

Que quelque chose d'autre peut être un FooFactory, voici une façon de le faire....

Vous FooFactory devrait ressembler à ceci:

// Notice the private FooFactory constructor
private FooFactory() {
}

public static FooFactory create() {
    return new FooFactory();
}

public FooFactory withColor( final Color col ) {
    this.color = color;
    return this;
}

public Foo build() {
    return new FooImpl( color, and, all, the, other, parameters, go, here );
}

60voto

Martin Odersky Points 13161

Dans Scala 2.8, vous pouvez utiliser des paramètres nommés et par défaut, ainsi que la méthode copy sur une classe de cas. Voici un exemple de code:

 case class Person(name: String, age: Int, children: List[Person] = List()) {
  def addChild(p: Person) = copy(children = p :: this.children)
}

val parent = Person(name = "Bob", age = 55)
  .addChild(Person("Lisa", 23))
  .addChild(Person("Peter", 16))
 

20voto

Daniel C. Sobral Points 159554

Eh bien, considérons ceci sur Scala 2.8:

 case class Person(name: String, 
                  married: Boolean = false, 
                  espouse: Option[String] = None, 
                  children: Set[String] = Set.empty) {
  def marriedTo(whom: String) = this.copy(married = true, espouse = Some(whom))
  def addChild(whom: String) = this.copy(children = children + whom)
}

scala> Person("Joseph").marriedTo("Mary").addChild("Jesus")
res1: Person = Person(Joseph,true,Some(Mary),Set(Jesus))
 

Cela a son lot de problèmes, bien sûr. Par exemple, essayez de créer espouse et Option[Person] , puis de marier deux personnes. Je ne vois pas comment résoudre ce problème sans recourir à un constructeur private var et / ou private plus une usine.

11voto

Voici quelques autres options:

Option 1

Rendre l'implémentation elle-même mutable, mais séparer les interfaces qu'elle expose à mutable et immuable. Ceci est tiré de la conception de la bibliothèque Swing.

 public interface Foo {
  X getX();
  Y getY();
}

public interface MutableFoo extends Foo {
  void setX(X x);
  void setY(Y y);
}

public class FooImpl implements MutableFoo {...}

public SomeClassThatUsesFoo {
  public Foo makeFoo(...) {
    MutableFoo ret = new MutableFoo...
    ret.setX(...);
    ret.setY(...);
    return ret; // As Foo, not MutableFoo
  }
}
 

Option 2

Si votre application contient un ensemble volumineux mais prédéfini d'objets immuables (par exemple, des objets de configuration), vous pouvez envisager d'utiliser le cadre Spring .

5voto

bmargulies Points 49855

Quatre possibilités:

new Immutable(one, fish, two, fish, red, fish, blue, fish); /*1 */

params = new ImmutableParameters(); /*2 */
params.setType("fowl");
new Immutable(params);

factory = new ImmutableFactory(); /*3 */
factory.setType("fish");
factory.getInstance();

Immutable boringImmutable = new Immutable(); /* 4 */
Immutable lessBoring = boringImmutable.setType("vegetable");

Pour moi, chacun des 2, 3 et 4 est adapté à une différence de situation. Le premier est difficile à l'amour, pour les raisons évoquées par les OP, et est généralement un symptôme d'une conception qui a subi quelques de fluage et a besoin de peu de refactoring.

Ce que je suis sur la liste (2) c'est bien quand il n'y a pas d'état de l'origine de la "factory", alors que (3) est le modèle de choix quand il est dans l'état. Je trouve moi-même à l'aide de (2) plutôt que de (3) lorsque je ne veux pas à vous soucier de threads et de synchronisation, et je n'ai pas besoin de vous soucier d'amortissement de certains cher de configuration au cours de la production de la plupart des objets. (3), d'autre part, est appelé lorsque un réel travail va dans la construction de l'usine (mise en place d'un SPI, la lecture de fichiers de configuration, etc).

Enfin, quelqu'un d'autre répondre à option (4), où vous avez beaucoup de petits objets immuables et le meilleur modèle est d'obtenir des nouvelles de ceux de la vieille.

Notez que je ne suis pas un membre de la "modèle fan club' -- bien sûr, certaines choses valent la peine d'imiter, mais il me semble qu'ils prennent un peu serviable vie qui leur est propre une fois que les gens de leur donner des noms et de drôles de chapeaux.

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