Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructor: Full fledged or minimal?

When designing classes you usually have to decide between:

  • providing a "full" constructor that takes the initial values for all required fields as arguments: clumsy to use but guarantees fully initialized and valid objects
  • providing just a "default" constructor and accessors for all necessary fields: might be convenient sometimes but does not guarantee that all members are properly initialized before some critical methods are called.
  • a mixed approach (more code, more work, can' eliminate the "not fully initialized" problem)

I have seen several APIs and frameworks that use one of the above or even an inconsistent approach that differs from class to class. What are your thoughts and best practices on that subject?

like image 997
rluba Avatar asked Jul 12 '26 00:07

rluba


2 Answers

The short answer is that an object should be fully initialized after its constructor was called.

This should be the default approach with the least surprises for users. There are cases when your run-time, framework or other technical constraints prevent the default approach.

In some circumstances a the Builder pattern helps to support cases when it is not possible to use a simple constructor. This approach is in the middle ground, letting users call setters to initialize and still be able to work only with fully initialized objects.

A static factory method is appropriate in cases when the object constructor needs to be more flexible than a constructor but a builder is too complex to implement.

Constructor:

x = new X(a, b);

Setter:

x = new X();
x.setA(a);
x.setB(b);

Builder:

builder = new Builder();
builder.setA(a);
builder.setB(b);
x = builder.build();

Static factory method:

x = X.newX(a, b);

All four approaches will produce an instance, x, of class X.

Constructor

Pros:

  • Simple

Cons:

  • May be not possible due to constraints
  • May need too many constructor arguments (named arguments and default values can help here if the languages supports them: new X(a = "a", b = "c"))

Setter

Pros:

  • Moderate complexity
  • May be mandated by framework

Cons:

  • Instances may not be fully initialized

Builder

Pros:

  • Most flexible approach
  • May reuse instances (using singletons, flyweights and caches internally)

Cons:

  • Most complex to implement
  • May produce run-time exceptions for invalid initialized objects
  • Overhead of the builder object instance

Static factory method

Pros:

  • Moderate complexity
  • May reuse instances (using singletons, flyweights and caches internally)

Cons:

  • More complex to implement and use than constructor
like image 125
Thomas Jung Avatar answered Jul 15 '26 21:07

Thomas Jung


Definitely "full" in all but the edgiest of edge cases.

It's very straightforward to me, that the point of a constructor is to set up the object so that it's ready to use. Obviously immutable objects are by far the best, but it's also often acceptable for some types of object to change state later. One thing to avoid, however, is having on object that can be constructed at one point, but an init() or setup() method must be called on it before use. It's irritating, confusing - and what's the point of having an object constructed if it's illegal to call "real" methods on it?

To my mind there are no substantial downsides to requiring "full" construction; the callers would have to marshall all the required arguments together before being able to use the object anyway, and making them do so in the constructor removes a whole class of errors. It's great to know that if you're passed an instance of an object it will be valid for use, rather than having some temporal and/or state dependency. If anything, this can also make it clearer to calling code exactly what dependencies need to be gathered by having them all defined in one clear place.

In fact the only concrete argument I can see for not doing this is circular dependencies; if class A requires a B and vice versa, then one or other of these relationships must be fulfilled via a setter method. Whether this represents good design is left as an exercise for the reader. :-)

like image 22
Andrzej Doyle Avatar answered Jul 15 '26 21:07

Andrzej Doyle



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!