featureenvy

The blog of a thinkerer.
By @featureenvy

All rights reserved.

Are Your Abusing Constructors, Too?

Ah, constructors. The little, forgotten, and often abused thing. No one really thinks about it for long, no one really knows what it does. Everyone tends to ignore it until they think they need it and throws things into it with no care whatsoever. Yes, the constructor might be the best equivalent to a homeless child. And yet, recently, I started thinking more and more about homeless children constructors. They are a defining character of all object-oriented programming languages. And they do fulfill an important role! So let's learn a bit more about the homeless constructor.

Do's in a Constructor

Ever had a class that felt awkward to use? Where you, in practically ever method, first checked if some precondition was fulfilled? That might mean you have forgotten to put something in a constructor. The simple rule is: Everything the class needs in order to work goes into the constructor. For example, recently I wrote a small proxy to Nokogiri. The class looked something like this:
class XmlHelper
  def open(file)
    @file = Nokogiri::XML(file)
  end

  def find_uas
    raise 'Open the file first' unless @file
    # read file etc.
  end
end
With one method, that might work. But how would I add a second method here? I would have to do the same check if the file is actually opened again. Which means the class isn't ready for operation after the constructor is called! So I fixed this the easy (but hey, stylish!) way:
class XmlHelper
  def initialize(file)
    @file = Nokogiri::XML(file)
  end

  def find_uas
    raise 'Open the file first' unless @file
    # read file etc.
  end
end
Yes, one small change. From now on, when someone wants to make an instance of XmlHelper, he has to provide a file to read. Which means the class is ready to be used.
Put things into the constructor if the class can't feasibly be used without it.
Another great use of constructors? Dependencies! Of course, you could add a dependency like that:
class Limb
  @child = Child.new
end
We have two problems with this:
  1. The Limb class knows too much about its surroundings. Maybe it just needs to tell the child that it is tired. But what if we suddenly want to use the Limb class with an Adult? A dog? We can't do that with this implementation!
  2. Another problem is testing. We can't, in a unit test, easily replace Child with a mock or a stub. Maybe instantiation of a child is difficult (it takes 9 months for gods sake!) Or we simply want to test the interaction between Limb and Child, which is difficult if we don't know to which Child this Limb is talking to. (Remember: A constructor shouldn't do real work!)
The better way to do it?
class Limb
  def initialize(body)
    @body = body
  end
end
Ah, now it's obvious, right? We can simply add a new body that this Limb belongs to. And the Limb doesn't care anymore if it is a child or not. For the lazy who don't want to type Limb.new(child) all the time, we can also change the initialize call to def initialize(body = Child.new) ... end so the Child is automatically added if we don't specify something else.

But Don't Put These Things Into the Constructor!

So now that we have a usable object after construction, what doesn't belong in there? Should we just throw everything into there? Now that we know what to throw into a constructor, it is easy what not to throw into a constructor. In short: Everything else. If not every method in the class needs this property, if the class doesn't need this information to work correctly, then don't add it to the constructor! If some methods need a property, and the others don't, you probably should split the class up into two. They deal with different things. One special thing though: If the initialization logic is very complex, it might be better to put it into a builder. A builder is a separate object that can be used to build up an object. It can even build objects of different classes (that have the same method, of course) given different input.

Ah, So That's Constructors!

Yes, so it is. So please stop abusing the little homeless kid on the block and treat it with the kindness it deserves. It shouldn't be dropped all responsibilities, but don't forget that it is there and you can use it. To summarize: Pass a constructor all the information the class needs to work properly, but nothing more. Have I forgotten some use case for constructors? Do you know some other constructor abuse cases? Then drop a line either here in the comments or on Twitter! If you liked this post, you should consider hiring me freelance, part-time or full-time!