Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How much work should be done in a constructor?

Should operations that could take some time be performed in a constructor or should the object be constructed and then initialised later.

For example when constructing an object that represents a directory structure should the population of the object and its children be done in the constructor. Clearly, a directory can contain directories and which in turn can contain directories and so on.

What is the elegant solution to this?

like image 615
mintydog Avatar asked Nov 16 '08 15:11

mintydog


People also ask

How big should a constructor be?

As little as is needed to complete the initialization of the object. If you can talk about a portion (5 or so lines is my guideline) of your constructor as a chunk of logic or a specific process, it's probably best to split it into a separate method for clarity and organizational purposes.

Is doing a lot in constructors bad?

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. I think that your problems may have been caused by violation of SRP and not the constructor doing actual work. – Emily L.

What is the work of the constructor?

Constructors often have the same name as the declaring class. They have the task of initializing the object's data members and of establishing the invariant of the class, failing if the invariant is invalid. A properly written constructor leaves the resulting object in a valid state.

What should you do in a constructor?

The purpose of constructor is to initialize the object of a class while the purpose of a method is to perform a task by executing java code. Constructors cannot be abstract, final, static and synchronised while methods can be. Constructors do not have return types while methods do.


13 Answers

To summarize:

  • At a minimum, your constructor needs to get the object configured to the point that its invariants are true.

  • Your choice of invariants may affect your clients.(Does the object promise to be ready for access at all times? Or only only in certain states?) A constructor that takes care of all of the set-up up-front may make life simpler for the class's clients.

  • Long-running constructors are not inherently bad, but may be bad in some contexts.

  • For systems involving a user-interaction, long-running methods of any type may lead to poor responsiveness, and should be avoided.

  • Delaying computation until after the constructor may be an effective optimization; it may turn out to be unnecessary to perform all the work. This depends on the application, and shouldn't be determined prematurely.

  • Overall, it depends.

like image 180
Oddthinking Avatar answered Oct 02 '22 08:10

Oddthinking


You usually do not want the constructor to do any computation. Someone else using the code will not expect that it does more than basic set-up.

For a directory tree like you're talking about, the "elegant" solution is probably not to build a full tree when the object is constructed. Instead, build it on demand. Someone using your object may not really care what is in sub-directories, so start out by just having your constructor list the first level, and then if someone wants to descend into a specific directory, then build that portion of the tree when they request it.

like image 42
SoapBox Avatar answered Oct 02 '22 07:10

SoapBox


The time required should not be a reason not to put something into a constructor. You could put the code itself into a private function, and call that out of your constructor, just to keep the code in the constructor clear.

However, if the stuff you want to do is not required to give the object a defined condition, and you could do that stuff later on first use, this would be a reasonable argument to put it out and do it later. But don't make it dependant on the users of your class: These things (on-demand initialization) must be completely transparent to users of your class. Otherwise, important invariants of your object might easily break.

like image 43
Johannes Schaub - litb Avatar answered Oct 02 '22 07:10

Johannes Schaub - litb


It depends (typical CS answer). If you are constructing objects at startup for a long-running program, then there is no problem with doing a lot of work in constructors. If this is part of a GUI where fast response is expected, it might not be appropriate. As always, the best answer is to try it the simplest way first, profile, and optimize from there.

For this specific case, you can do lazy construction of the sub-directory objects. Only create entries for the names of the top level directories. If they are accessed, then load the contents of that directory. Do this again as the user expolres the directory structure.

like image 31
KeithB Avatar answered Oct 02 '22 07:10

KeithB


The most important jobs of a constructor is to give the object an initial valid state. The most important expectation on the constructor, in my opinion, is that the constructor should have NO SIDE EFFECTS.

like image 21
Hugo Avatar answered Oct 02 '22 06:10

Hugo


I would agree that long running constructors are not inherently bad. But I would argue that thy are almost always the wrong thing to do. My advice is similar to that from Hugo, Rich, and Litb:

  1. keep the work you do in constructors to a minimum - keep the focused on initializing state.
  2. Don't throw from constructors unless you cannot avoid it. I try to only throw std::bad_alloc.
  3. Don't call OS or Library APIs unless you know what they do - most can block. They will run quickly on your dev box and test machines, but in the field they can be blocked for long periods of time as the system is busy doing something else.
  4. Never, ever do I/O in a constructor - of any kind. I/O is commonly subject to all kinds of very long latencies (100's of milliseconds to seconds). I/O includes
    • Disk I/O
    • Anything that uses the network (even indirectly) Remember most resources can be off box.

I/O problem example: Many hard disks have a problem where they get into a state where they do not service reads or writes for 100's or even thousands of milliseconds. The first and generation solid state drives do this often. The user has now way of knowing that your program jus hung for a bit - they just think it is your buggy software.

Of course, the evilness of a long running constructor is dependent on two things:

  1. What 'long' means
  2. The how often in a given period objects with the 'long' constructors are constructed.

Now, if 'long' is simply a few 100 extra clock cycles of work, then its not really long. But a constructor is getting into the 100's of microsecond range the I suggest it is pretty long. Of course, if you are only instantiating one of these, or instantiating them rarely (say one every few seconds) then you are not likely to see problms due to a duration in this range.

Frequency is an important factor, a 500 us ctor isn't a problem if you are only building a few of them: but creating a million of them would pose a significant performance problem.

Lets talk about your example: populating a tree of directory objects inside "class Directory" object. (note, I'm going to assume this is a program with a graphical UI). Here, your CTOR duration isn't dependent on the code you write - its defendant on the time it takes to enumerate an arbitrarily large directory tree. This is bad enough on local hard drive. Its even more problematic on remote (networked) resurce.

Now, imagine doing this on your user interface thread - your UI will stop dead in its tracks for seconds, 10's of seconds or potential even minutes. In Windows we call this a UI hang. They are bad bad bad (yes we have them... yes we work hard to eliminate them).

UI Hangs are something that can make people really hate your software.

The right thing to do here is simply initialize your directory objects. Build your directory tree in a loop that is can be canceled and keeps your UI in a responsive state ( the cancel button should always works)

like image 23
Foredecker Avatar answered Oct 02 '22 07:10

Foredecker


For the sake of code maintenance, testing, and debugging I try to avoid putting any logic in constructors. If you prefer to have logic execute from a constructor then it is helpful to put the logic in a method such as init() and call init() from the constructor. If you plan on developing unit tests you should avoid putting any logic in a constructor since it may be difficult to test different cases. I think the previous comments already address this, but... if your application is interactive then you should avoid having a single call that leads to a noticeable performance hit. If your application is non-interactive (ex: nightly batch job) then a single performance hit isn't as big a deal.

like image 31
rich Avatar answered Oct 02 '22 08:10

rich


Historically, I have coded my constructors so that the object is ready to use once the constructor method is complete. How much or how little code is involved depends on the requirements for the object.

For example, let's say I need to display the following Company class in a details view:

public class Company
{
    public int Company_ID { get; set; }
    public string CompanyName { get; set; }
    public Address MailingAddress { get; set; }
    public Phones CompanyPhones { get; set; }
    public Contact ContactPerson { get; set; }
}

Since I want to display all the information I have about the company in the details view, my constructor will contain all of the code necessary to populate every property. Given that is a complex type, the Company constructor will trigger the execution of the Address, Phones and Contact constructor as well.

Now, if I am populating a directory listing view, where I may only need the CompanyName and the main phone number, I may have a second constructor on the class that only retrieves that information and leaves the remaining information empty, or I may just create a separate object that only holds that information. It really just depends on how the information is retrieved, and from where.

Regardless of the number of constructors on a class, my personal goal is to do whatever processing is necessary to prepare the object for whatever tasks may be imposed upon it.

like image 45
Neil T. Avatar answered Oct 02 '22 08:10

Neil T.


As for how much work should be done in the constructor, I'd say it should take into account how slow things are, how you are going to use the class, and generally how you feel about it personally.

On your directory structure object: I recently implemented a samba (windows shares) browser for my HTPC, and as that was incredibly slow I opted to only actually initialize a directory when it was touched. e.g. First the tree would consist of just a list of machines, then whenever you browse into a directory the system would automatically initialize the tree from that machine and get the directory listing one level deeper, and so on.

Ideally I think you could even take it as far as writing a worker thread that scans the directories breadth-first, and would give priority to the directory you're currently browsing, but generally that's just too much work for something simple ;)

like image 39
Frans-Willem Avatar answered Oct 02 '22 08:10

Frans-Willem


Make sure the ctor does nothing that could throw an exception.

like image 45
EricSchaefer Avatar answered Oct 02 '22 08:10

EricSchaefer


RAII is the backbone of C++ resource management, so acquire the resources you need in the constructor, release them in the destructor.

This is when you establish your class invariants. If it takes time, it takes time. The fewer "if X exists do Y" constructs you have, the simpler the rest of the class will be to design. Later, if profiling shows this to be a problem, consider optimizations like lazy initialization (acquiring resources when you first need them).

like image 34
christopher_f Avatar answered Oct 02 '22 06:10

christopher_f


As much as necessary and no more.

The constructor must put the object into a usable state, hence at a minimum your class variables ought to be initted. What initted means can have a broad interpretation. Here is a contrived example. Imagine you have a class that has the responsibility of providing N! to your calling application.

One way to implement it would be to have the constructor do nothing, with a member function with a loop that calculates the value needed and returns.

Another way to implement it would be to have an class variable which is an array. The constructor would set all the values to -1, to indicate that the value has not been calculated yet. the member function would do lazy evaluation. It looks at the array element. If it is -1, it calculates it and stores it and returns the value, otherwise it just returns the value from the array.

Another way to implement it would be just like the last one, only the constructor would precalculate the values, and populate the array, so the method, could just pull the value out of the array and return it.

Another way to implement it would be to keep the values in a text file, and use N as the basis for an offset into the file to pull the value from. In this case, the constructor would open the file, and the destructor would close the file, while the method would do some sort of fseek/fread and return the value.

Another way to implement it, would be to precompute the values, and store them as a static array that the class can reference. The constructor would have no work, and the method would reach into the array to get the value and return it. Multiple instances would share that array.

That all being said, the thing to focus on, is that generally you want to be able to call the constructor once, then use the other methods frequently. If doing more work in the constructor means that your methods have less work to do, and run faster, then it is a good trade off. If you are constructing/destructing a lot, like in a loop, then it is probably not a good idea to have a high cost for your constructor.

like image 43
EvilTeach Avatar answered Oct 02 '22 07:10

EvilTeach


If something can be done outside a constructor, avoid doing it inside. Later, when you know your class is otherwise well-behaved, you might risk doing it inside.

like image 29
Aydya Avatar answered Oct 02 '22 06:10

Aydya