7

This question already has an answer here:

Several times now I have come across the situations where you have some kind of settings class that simply contains a mass of data. Often these classes are simply not valid without at least most of the data.

However a constructor like this:

public dataclass (int v1, int v2, int v3, int v4,
      string v5, double v6, string v7, int v8, 
      Internalclass v9, string v10,
      double v11, double v12, int v13, int v14)
{
   this.v1=v1;
   this.v2=v2;
   ...
   this.v14=v14; 
}

feels like it's not good code practice. Now I could try to group some of these variables together but often these variables only make sense in the context of the data class. So what is a good way to design such a data class?

marked as duplicate by gnat, user53019, JB King, user40980, user22815 Nov 9 '15 at 16:49

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

13

You are looking for the builder pattern.

Essentially it's an object with setters where you can set the parameters for construction and a createResult() method that will create the object you are looking for (or fail if it wouldn't be valid).

You can use it to contain the defaults for the properties and group some of them into a single setter.

DataClassBuilder builder = new DataClassBuilder();

builder.setV1(v1);
builder.setV3(v3);
builder.setV4(v4);
builder.setV5AndV6(v5, v6);
//...

DataClass data = builder.createResult();
  • 1
    I think the last sentence (and the setV5AndV6() method) are a key point to remember, though. Some of the parameters may be logically grouped. I've seen some Builders implemented where every parameter was set by the same method, yet some were guaranteed to be generated together because of the context. That forced me to have multiple method calls where there should likely have been one if this example was followed. It does depend on your domain, though. – Thomas Owens Nov 9 '15 at 13:02
  • A problem with this solution (contrasted with the constructor in the question) is that with the constructor, if you're not setting a property, you will know at compile time, whereas with this solution, you can write valid code that will fail at runtime. Of course, if only some parameters are required, then the builder pattern may be preferable. – Mike Harris Nov 9 '15 at 14:31
  • 1
    It takes a bit more work, but you can add compile time safety to a builder. One way of doing this is via a multi stage builder, which only returns a buildable object when all parameters have been set. It's considerably more work, though. – Morgen Nov 9 '15 at 14:40
  • @Mike Harris: a builder still can have mandatory parameters needed to construct the builder. It’s also possible to have factory methods for constructing the builder, so that you can have alternative sets of mandatory parameters. Without optional or alternative parameters, the builder becomes less useful… – Holger Nov 9 '15 at 15:17
5

Are all those parameters actually required? By "data class" do you mean a Domain Model, View Model or Data Transfer Object? Requiring such a large list of arguments for a simple "data class" starts to feel like a Code Smell telling you either:

  1. This class is doing too much, and much of its work should be delegated to other classes that get passed in as a dependency

  2. Not everything is needed. Optional values in data classes should not be passed in via the constructor

I think the question here is not "How do I handle a large list of constructor arguments" and instead is "How can I refactor my object model so I don't need two dozen parameters to a constructor."

The Builder Pattern will "fix" your problem, but I think it is putting a bandage on a larger problem that requires a different solution.

  • Well the type of "data class" I had in mind might be something like projects settings or the data that is being sent over the network. – Thijser Nov 9 '15 at 13:43
  • @Thijser: A "project settings" object could be viewed as a Domain Model, and "data being sent over a network" is a classic example of a Data Transfer Object. – Greg Burghardt Nov 9 '15 at 14:04
  • 1
    "Optional values in data classes should not be passed in via the constructor" -- There isn't really an alternative to constructors, if you want immutability. Of course one can prettify the code by using builders, pseudo-mutators or compiler level syntax sugar, but all of these boil down to passing all independent properties to a constructor. The first point, that you should avoid too many properties in the first place, remains valid. – CodesInChaos Nov 9 '15 at 14:49
0

If your parameters are optional, the Builder pattern could be useful indeed! Though, the builder pattern can be a mess sometimes. If you use the Builder at many places, when you'll add a new parameter you'll need to make sure you don't forget to add it in every places because you won't have compile-time check.

If the parameters are not optional, you would need to verify everything has been set at the moment to call the Build method!

I'd like to propose another solution, the Introduce Parameter Object refactor.

The idea is to group your parameters (if possible) by some logic. Then, you'd have less parameters in this constructor and maybe 3-4 more "parameter classes". You could argue this moves the problem elsewhere, but it gives you more flexibility on how to create these parameter objects.

Maybe one of the "parameter object" can be created via an hardcoded factory, the other could be retrieved from a configuration file etc.

This way you create a better separation of concerns!

Not the answer you're looking for? Browse other questions tagged or ask your own question.