views:

118

answers:

6

I have a type with about 40 properties (all value types) that represents a type of transaction for my business. An instance of this class corresponds to a row in my database. I would like to keep my class immutable since it will only ever be used for read operations, but I am not sure how to go about setting 40 properties during initialization.

Typically I use constructor initialization for immutable types, but I would like to avoid writing a constructor with 40 parameters. The setters for my properties are currently private though I am willing to change with good enough reason. Is there a common way to handle this situation or a better way to approach the problem?

+6  A: 

Quick point. You mentioned your setters on the object are private. If that is the case then your object is not immutable (otherwise setters couldn't exist). At best your object is read only.

For a true immutable object there is no choice but to have the constructor take in all of the values necessary to initialize the object. The best way to reduce the number of parameters in the constructor is to group the values into bigger objects which are then passed to the constructor. Although I wouldn't do that unless the values are otherwise logically related.

If your immutable type does truly need the 40 values and they are otherwise unrelated, the best approach is to have a constructor with 40 values. That or further break down the big immutable object.

JaredPar
40 values... my head is going to explode.
pst
@pst, I've seen that before but most often in generated code.
JaredPar
Named parameters can make this a bit less painful. Of course you need to properly name them.
ChaosPandion
Thanks for pointing out that distinction. I should be able to group the fields down into a few different objects. Thanks for your post.
NickLarsen
+9  A: 

Your problem isn't so much a constructor with 40 arguments, but a class with 40 fields.

I'd recommend breaking this down. Are any of the fields related in some way? If so, group them into a common object (e.g. EmailInfo), then have your big object just reference the grouping objects.

// Instead of this:
foo.EmailHeader
foo.EmailSubject
foo.Email...

// Do this:
foo.Email.Header
foo.Email.Subject

Once your class has fewer direct properties, creating a constructor that takes those grouping objects isn't so terrible.

Judah Himango
I definitely agree with this. A class that stores 40 pieces of data that can't be put into a list or other container? Poor design.
DeadMG
Thats a great idea.
NickLarsen
+1. Agree. 40 attributes that cannot be grouped in other objects really smell.
bloparod
+2  A: 

I like the approach of using a mutable object to instantiate an immutable object; the mutable object is just for tidy passing of options. One example of this in the .NET framework is ProcessStartInfo.

class XInfo {
  public int A;
  public int B;
}

class X {
  public X (XInfo i) {
    // you can transform the data/layout from i any way you need
    ..
  }
}

new X(new XInfo() {
  A = 42
})

While I'll hold my tongue about the '40 properties', I find the above approach works pretty well. An added bonus is the XInfo and the internal structure used in X can be entirely different, as long as you can provide a sane mapping.

pst
This is the Builder pattern. `StringBuilder` and `XmlReaderSettings` are two more examples.
Stephen Cleary
@Stephen - The builder patten can be a bit annoying to set up but it does work.
ChaosPandion
A: 

As an alternative, you can make your class derive from freezable, I think this may be the solution you are searching for. You can Instatiate the object, set the values, then set it frozen. Once you set it frozen the class is 'read only'.

Jonathan
+1  A: 

If i go by your words "but I am not sure how to go about setting 40 properties during initialization.", it appears that your problem is a class with too many fields/properties. Doesnt seem to be a problem of making it immutable, because you already know how to do that.

I would suggest (like others), Refactor and Extract Class.

Amby
A: 

I would suggest putting the parameters into one or more structures, and having the object hold those structures. Nesting objects would be possible, but would add more overhead than nesting structures.

As an alternative, you could create an abstract base class with "readonly mustoverride" versions of all your properties. From this, derive a mutable and immutable object class. The immutable one can accept the base class in its constructor, and use all the readonly properties to build the new object. The mutable class can provide a means of writing the properties using methods, read-write properties with different names from the readonly versions, etc.

supercat