75

Making all fields final is in general a good idea, but sometimes I find myself doing everything in the constructor. Recently I ended up with a class doing actually everything in the constructor, including reading a property file and accessing a database.

On one hand, this is what the class is for, it encapsulates the data read and I like creating objects completely initialized. The constructor is not complicated at all as it delegates most of the work, so it looks fine.

On the other hand, it feels a bit strange. Moreover, in this talk at about 17:58 there are good reasons for not doing much work in constructor. I think I can eliminate the problem by passing appropriate dummies as constructor arguments.

The question remains: Is doing a lot of work (or even all the work) in constructors bad?

maaartinus
  • 44,714
  • 32
  • 161
  • 320
  • 2
    See http://stackoverflow.com/questions/531282/how-much-code-should-one-put-in-a-constructors-java. I would never put a DB call into a constructor, though - consider using a static factory method in a separate factory class. – Matt Ball Aug 13 '11 at 04:15

5 Answers5

46

I think that "Doing work in the constructor" is okay...

... as long as you don't violate Single Responsibility Principle (SRP) and stick to using Dependency Injection (DI).

I have been asking myself this question too lately. And the motivation against doing work in the constructor that I have found are either:

  • It makes it hard to test
    • All examples I have seen have been where DI wasn't used. It wasn't actually the fault of the constructor doing actual work.
  • You might not need all the results that your constructor calculates, wasting processing time and it's hard to test in isolation.
    • This is basically a violation of SRP, not a fault of the constructor doing work per say.
  • Old compilers have/had trouble with exceptions thrown in constructors, hence you shouldn't do anything other than assign fields in constructors.
    • I don't think it's a good idea to write new code taking historical compiler deficiencies into account. We might as well do away with C++11 and all that is good all together if we do.

My opinion is that...

... if your constructor needs to do work for it to adhere to Resource Acquisition Is Initialization (RAII) and the class does not violate SRP and DI is properly used; Then doing work in the constructor is A-Okay! You can even throw an exception if you'd like to prevent usage of a class object whose initialization failed completely instead of relying on the user to check some return value.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Emily L.
  • 5,673
  • 2
  • 40
  • 60
  • 2
    Apparently I had my mind set in C++ when I answered, oh derp. nvm still applies ;) – Emily L. Oct 02 '13 at 14:54
  • "return values" would be a through a public member because constructors are not allowed to return a value. – v.oddou Sep 22 '14 at 07:55
  • @v.oddou Or through the argument passed (by reference) to the constructor. But if a constructor is indeed a function, the same rule ought to apply to ALL other functions ... Its beginning to look like a functional programming, If you're working with an FP language. – Olumide Feb 02 '18 at 18:35
21

This is a very open-ended question, so my answer will try to be as general as possible...

Doing work in constructors isn't as "bad" as it used to be years ago when exception handling wasn't as prevalent and evolved as it is today. You'll notice that the Google Tech talk primarily looks at constructors from a Testing perspective. Constructors have been historically very very difficult to debug so the speaker is correct that doing as little as possible in a constructor is better.

With that said, you'll notice that he's also touching on dependency injection/the provider pattern which is notorious for complicating constructors. In such a case, leaving ONLY provider/DI code in the constructor is preferred. Again, the answer depends on what patterns you're using and how your code "fits" together.

The entire point of using a constructor is to create a neat object that can be used immediately; i.e. new Student("David Titarenco", "Senior", 3.5). There's no need to do david.initialize() as it would be entirely silly.

Here's some of my production code, for example:

    Config Conf = new Config();
    Log.info("Loading server.conf");
    Conf.doConfig();

In the above case, I decided not to do anything with the constructor (it's empty) but have a doConfig() method that does all the disk i/o; I've often thought that the doConfig() method is just pointless and I should do everything in the constructor. (I only check out the config file once, after all.)

I think that it's entirely dependent on your code and you shouldn't think that putting "stuff" in your constructor is a bad thing. That's what constructors are for! Sometimes we get carried away with OOP (getThis, setThat, doBark) when really all a class needs to do is a load a config file. In such cases, just put everything in the constructor and call it a day!

David Titarenco
  • 32,662
  • 13
  • 66
  • 111
  • 2
    Your config example is flawed as it violates the SRP. What if you could store the config in a RDBMS instead of a file? `Config config = new FileConfigurationProvider('server.conf').getConfig();` ... and call it a day! No work in constructors and a better design. – plalx Nov 26 '15 at 15:19
  • 4
    Ah, all those "what ifs" that never happen... Don't overengineer your code. That's what refactors are for. – rr- Jun 14 '16 at 10:20
  • 1
    For smaller chunks of example codes, yeah, call it a day and do whatever. But in larger production code-bases these are just skeletons in the cupboard. So this solution is great until some colleague starts calling 'new Config()' in random parts of the code, causing some disk I/O and multiple instances of config objects. – pcjuzer May 31 '20 at 12:56
19

I faced the following problems when I put too much code into the constructor:

  • It was hard to write unit tests for other methods of that class, because it wanted to do a lots of things in the constructor, thus, I had to set up a lots of valid things or at least mocks (DB, file, whatever) for the simplest unit tests.
  • It was hard to write unit tests for the constructor itself. Anyway, putting a lots of code with diversified responsibilites into one block is even a bad idea. (Single Responsibility Principle.)
  • For the former reasons, it was hard to use that class. For example, it completely prevented me to implement some deferred init methods, because it needed everything in the moment of invoking the constructor. Ok, I could write the lazy init method into the constructor, nice.
  • Sooner or later I realized that it had sense to reuse some code parts which are placed in the constructor. Well, when I first wrote the constructor I also thought that those code parts will be used just there, forever.
  • When I wanted to extend that class and insert some logic before or into the super constructor's logic, it simply didn't work because the first thing to do in the extended class' constructor is to invoke the super's one.

So yes, doing a lot in constructor is a bad idea in my opinion.

Generally I just put some field initializations into the constructor and make an init method to invoke when everybody is on board.

Sahil Doshi
  • 1,073
  • 10
  • 23
pcjuzer
  • 2,724
  • 4
  • 25
  • 34
  • 1
    I think that your problems may have been caused by violation of SRP and not the constructor doing actual work. – Emily L. Sep 27 '13 at 12:57
  • Generally, 'doing a lot' significantly increases the chance of violating SRP. – pcjuzer Jul 19 '16 at 13:17
  • 2
    Sure, but doing a lot is not the problem. Violating SRP is the problem. – Emily L. Jul 19 '16 at 16:00
  • Every one of your points is about tradeoffs, and are not specifically good or bad, but don't really imply work in the constructor being bad. Your point about thinking the code would only be used there forever should be the default premise until found to be false. You should not abstract until there's a reason to. The reasons are varied. Readability, re-usability, etc. But if there's not a good reason, then leave it where it is. Most objects, particularly value objects, should be stateless, implying all work is done in the constructor. That's good coding practice. – Trenton D. Adams Mar 25 '20 at 22:49
  • SRP doesn't mean no work in the constructor. It means if something can be re-used, it should be moved out of the constructor but still used in the constructor. – Trenton D. Adams Mar 25 '20 at 22:50
  • @TrentonD.Adams I wrote five of my experiences and they aren't made up or abstract, they are real. I also didn't write those are 'bad' but those were 'bad ideas', looking back. However, I'm very open, so I'd love to see practical examples when doing a lot in a constructor happened to be a great idea. Value objects aren't stateless, but usually they carry an immutable state. Initializing that state is not a lot, it's actually one thing even if the value consists of more parameters. However, in case of many params, using a Build pattern might be a better choice than exposing a constructor. – pcjuzer May 31 '20 at 12:20
  • @pcjuzer I didn't say your points were not valid, just that they are tradeoffs. Here's one example. You said... Sooner or later I realized that it had sense to reuse some code... My point here would be that when the "sooner or later I realized" comes, that is when you should move it out of the constructor, not before. i.e. do the least possible work to get the job done. If you need to re-use now, then make that re-usable, not before. So, your point is quite valid, but if you can't see a point of re-use right now, there's no use "guessing" how it might be re-used, just leave it. – Trenton D. Adams Jun 04 '20 at 18:44
  • 1
    I guess the main point I'm making is that "future proofing" is a bad practice. Solving the current problem in the simplest possible way is a good practice. So, "guessing" how something might be re-used is fundamentally not a good practice. If creating an abstraction (method, class, etc) adds to readability, or a "known" re-use scenario that you're going to use "right now", great, do it. e.g. If I see a 100 line constructor, I'm going to say "that's not readable, break it up into logic steps/private methods" – Trenton D. Adams Jun 04 '20 at 18:51
17

It usually is, if your object has a complicated creation algorithm you could probably simplify it using a Builder or a Factory. Specially if there are pre-conditions to be validated to build the object.

Once your start using Builders and Factories to build your objects they can validate the pre and post conditions and make sure clients of your code will only be able to access a fully initialized object and not a half-made one, you can even use the nowadays in vogue fluent interfaces to create your object and make it look cool ;D

new EmailMessage()
    .from("demo@guilhermechapiewski.com")
    .to("destination@address.com")
    .withSubject("Fluent Mail API")
    .withBody("Demo message")
    .send();

Obviously this isn't really your case, as this is not using a Builder, but it's much like something you could build to make your constructor do less work and make your code look simpler.

Maurício Linhares
  • 39,901
  • 14
  • 121
  • 158
2

Having constructors and destructors in my opinion is good, but not to do too much work in them. Especially not file/database access unless its very very specific to the class. You want to keep your constructors/destructors light to keep your program feeling fluid. Sometimes like already you have come to a case where the constructor does essentially all the work. There is a way to make things lighter. The concept/paradigm is called lazy evaluation. The idea is to accept inputs and do nothing (e.g. in constructor) but make use of the inputs when you need a calculation requested.

Example: Lets say you have a class that reads a file, parses it and tells you info like the sum of all numbers in the file. You can do this all in the constructor. Using lazy evaluation you will merely open the file, and have a getTotalSum() function. When called it will do the parsing and give you the result. This way you can also have getBestFit() to get best fit line. Sometimes you dont want to get best fit and for some inputs you do. This way the user will not be waiting for the constructor to do the calculations before the user decides what to do.

another example: lets say you have a view that loads 20 images. But only 5 are shown, and the constructor takes an array of the images to show. You can load them all in the constructor, but from a users perspective this will feel slow at the beginning. Or you can load 1 "loading" picture and load 1 image at a time. And as the user scrolls load more of the images on a as shown/needed basis.

Of course 1 problem is that you find out of errors like invalid pictures later down the road instead of the constructor. You can always perform simple checks for yourself to pre-validate the input to some degree (e.g. check for correct password).

over_optimistic
  • 1,399
  • 2
  • 18
  • 27