5

The internal framework my company uses has a pretty pivotal Object that has a pattern of being instantiated as a member of a class with a no argument constructor but to be usable you have to call a start method. I understand that constructors are generally minimalist but at the same time why be able to construct an unusable object that you just have to call start on anyway?

Basically this

public class Someclass{
    FrameworkThingy thingy = new FrameworkThingy();

    //Framework auto runs this method as part of Spring beans
    public void startUpMethod() {
        thingy.start();
        // Why not just do the start stuff in the constructor?
    }
}
ford prefect
  • 489
  • 7
  • 12
  • 2
    What happens if you want to subclass the object and do some stuff in the subclass start() before the parent start()? – pjc50 Oct 20 '16 at 15:32
  • @pjc50: in that case you'd still be out of luck unless Start() is virtual, and if it is going to be virtual that means you have additional design concerns to attend to. – whatsisname Oct 20 '16 at 19:40

5 Answers5

10

When I've seen this pattern in the past it's always been for one of the following reasons:

  1. Starting the class is an expensive operation. Leaving the starting logic in the constructor would slow down application bootstrapping/startup.
  2. Timing concerns. It could be that your class should never be started before another event in your application. So, instead of leaving the responsibility to Start to the class itself it may make more sense to hand that responsibility to a timing coordinator class.
  3. Stopping and Restarting are legitimate operations as well and only ever invoked explicitly. In this case, making Start be invoked implicitly in the constructor breaks the pattern and makes the class semantics a bit confusing.

Without any more specifics about your situation I can't really give you a more specific answer.

MetaFight
  • 11,549
  • 3
  • 44
  • 75
  • Also, I forgot to add: If it's possible for the **Start** operation to throw an exception, then invoking it from the constructor is a bad idea since constructors don't tend to throw exceptions. – MetaFight Oct 20 '16 at 19:32
  • 3
    It's pretty common for constructors to throw exceptions. If you subscribe to the idea that an object must be in a valid state at all times, and an object is unable to instantiate into a valid state, then throwing an exception within the constructor is your only option. – whatsisname Oct 20 '16 at 19:38
  • True. I guess the only thing that could (reasonably) trigger such an exception is if the constructor parameters are invalid. Parameter validation is definitely a good thing to do in a constructor. – MetaFight Oct 20 '16 at 19:42
3

Like many questions in software development, the answer again is "it depends".

Some of the articles other questions linked in answers to this advocate for 'lightweight' constructors that avoid doing any 'real work'. However, there are many circumstances where it is appropriate to initialize and begin doing work in the constructor.

Usually, it's appropriate when instantiation is cheap, when instantiating has essentially one conceptual successful outcome, and where there is nothing valid or meaningful to do between construction and a hypothetical start.

To start, let's characterize the idea of a Start() method is. In many situations it would instead be called Init() or Open() or similar. It would likely encompass stuff like opening or creating files, opening network ports or connections, connecting to a database, accessing hardware, starting new threads, etc. What are some situations where it would make sense to create an object and start performing those operations right away?

Take the example of something that deals with files on the file system. If we take the position that we should do minimal work in the constructor, then a typical usage is we instantiate our filewriter class, then later call Open() or similar, then later start providing data in some way. Since we want our object to be in a valid state at all times, we should verify we can access the file upon instantiation. If the file can't be accessed, we have to throw an exception upon construction. If everything is good to go, we can later call Open() and get to work. But wait, life is never that simple. We've created a race condition, as it's possible that between instantiation and Open(), the file has become inaccessible. So, we'd need to encase Open() in another try/catch to handle that scenario. How is that better that instantiating and opening in one operation? I'd argue that in this scenario the additional method is worse than doing the work in the constructor.

Even more complex instantiation procedures can be performed with paradigm in an easy to debug and test way, and dependency injection is often the means to do that. A database interface being provided an instantiated DatabaseConnection is a common example you may have seen in the wild. Instantiated, valid objects are progressively injected into objects using them, guaranteeing a valid state along the way.

Some comments and answers have stated that sometimes, you want to instantiate your object and later have it start doing its work, which to me makes little sense for cheap objects. It's pretty commonly stated to declare your variables as close to as when you're going to use them, why would that rule be different with objects? And if you do have them separate, what value are you providing yourself by keeping them separate? If you are opening a network port, at the moment you're ready, you have all the information you need to do so, so what real difference is there between a class that makes you call an additional method?

By doing the Start() work in the constructor, you are able to ensure that there is a single way to use a class, for that single way to be the right way, and for the compiler to enforce it for you. Having separate start methods, you have temporal coupling that trades off compile time programming errors for runtime errors. In a way, starting up in the constructor let's you reap some of the conceptual benefits of immutable objects, but for objects that do things.

Now, like I said, sometimes the answer is yes, sometimes it's no. Sometimes it does make sense to have an exposed Start() method. Sometimes you're interfacing with some other system where the RAII and OOP and similar paradigms don't exist, and there aren't exceptions, etc, and you have no choice. Sometimes instantiating is really expensive. In those cases and others it makes sense to have separate start methods. Only you can decide what's appropriate.

whatsisname
  • 27,463
  • 14
  • 73
  • 93
2

Because that's not what constructors are for. Constructors are for constructing. I don't expect or want objects to do anything other than get ready to be used when I construct them. If they start doing something I didn't ask them to do you're surprising me, the object user.

Separating use from construction is hard enough without dealing with objects that refuse to exist unless they can do something first. A constructor should get the object into a useable state. That's all. Any more is clutter.

candied_orange
  • 102,279
  • 24
  • 197
  • 315
  • You've contradicted yourself. For some objects, "started doing something" is the usable state. – whatsisname Oct 20 '16 at 16:35
  • 1
    @whatsisname No it's not. Constructors take in values. They can validate them and store them. That is trivial and does nothing to the outside world. `Start` is a verb. It should DO something. `SomeClass` shouldn't be doing something because it exists. It should wait to be asked to do something. – candied_orange Oct 20 '16 at 16:39
  • @CandiedOrange then in your opinion, should socket class open socket open connection on construction (which makes it usable) or just store resource string? (which means, it's not usable until you call open) – UldisK Oct 20 '16 at 17:42
  • When an object represents ownership of a resource (e.g. file handle, socket, thread, or mutex lock), it is generally reasonable to expect that the object is not only able to hold such a resource, but actually owns that resource – or refuses to be constructed. In my experience, such less mutable types make code much easier to reason about. One way to make our views agree is to prefer (static) factory functions for object construction, and make the constructors private. Then, the question where exactly the resource is acquired is a matter of coding convenience, not of dogma. – amon Oct 20 '16 at 18:21
  • @CandiedOrange: again, not always. In many platforms, creating a filestream sort of object *opens* the underlying file, and doesn't require an additional Open() call or anything of that sort, and obviously extends beyond storing and validating parameters called to it. The .NET StreamWriter works that way, for example. You might think it's poorly designed, but I think it works pretty well and doesn't surprise me. Where it's appropriate to require the Open() or Start() methods is usually when the process of starting is expensive. – whatsisname Oct 20 '16 at 19:32
  • Additionally the constructor "starting up" whatever it's going to do is the expected behavior when using RAII environments/paradigms so it's hardly just my opinion. – whatsisname Oct 20 '16 at 19:34
  • @whatsisname if java had destructors I might care about RAII but it doesn't. I was going to cite a bunch of sources that have made the argument for lightweight constructions but it turns out I've done it [before](http://stackoverflow.com/a/23623981/1493294). – candied_orange Oct 20 '16 at 19:45
  • @CandiedOrange Java might not have destructors, but it does have the AutoCloseable interface and the try-with-resource construct. Not quite as strict as C++, but this still allows you to use RAII if you want to (and can get all users of your class to cooperate…). – amon Oct 20 '16 at 20:06
  • 2
    @UldisK I would expect the socket class to open the connection _when I ask it to_. I may want to have the instance of the socket class created before then, but if your socket class goes ahead and opens the connection then that connection may be hanging around taking up resources for quite awhile until __I__ am ready for it to actually do something. – Jeff Lambert Oct 20 '16 at 21:31
2

Because Misko says you should avoid doing work in the constructor.

You are forcing me to think like you

You can't tell the future. I know this because I can't. How is your object going to be used? Can you assume it will be used that way, every single time? Maybe someone finds a use for it later on down the road that you hadn't imagined, but is practical. And it also requires that you do not call the start method. Since you moved that logic into the constructor they're just out of luck and an opportunity for code reuse is lost. Because you wanted to call a start method in the constructor.

Can I trust a new instance of your object?

The only constant in software development is that things change. I want a new instance of your object, and I want it right now. But you call a start method in the constructor. Then someone comes along and adds some more functionality into the start method, and now the instance that I wanted is completely different, even though no new dependencies have been introduced. I didn't change any of my code, but now it doesn't work and it's all your fault. Because you wanted to call a start method in the constructor.

I'm a programmer and that means I'm lazy

I have 28 things to do at work today. I know that I have to go to that meeting a 2:00pm. The meeting room is around the corner and down the hall, so I think I'll get there on time if I leave my desk at 1:58pm. I know that if I leave any earlier I will just be sitting there twiddling my thumbs while I wait for other people to get there. This is inefficient, which is the main reason for my laziness. Even if I'm just browsing programmers.stackexchange.com between 1:52pm and 1:58pm, that's a much better use of my time than just sitting in a conference room counting ceiling tiles. Besides, I may get an e-mail at 1:57pm saying that the meeting's cancelled, but if I left my desk at 1:55pm I wouldn't get it, and it would probably be around 2:05pm before I started getting a little anxious that I was the only one around, meaning that, because I wanted to be an eager go-getter, I actually wound up wasting an extra 10 whole minutes counting ceiling tiles. That's exactly what happened last time. There's not that many of them, so I counted them twice, just to be sure I was right (I was).

The point of this is that, although I need a new instance of your object now, I would much prefer it if you would just let me start the darn thing only when I'm good and ready and sure that I want it to actually start. If you automatically call start in the constructor, you are forcing me to construct that object at a particular point in time when you want me to. What if I want to construct it earlier for some reason, but at that point I'm just not quite ready to start? I can't, because you wanted to call a start method in the constructor.

Testing

This I think is the biggest reason. You need to mock an object to test against, but the internals of that object are modified (work is done) during the construction phase. With just a mock object, however, that code doesn't get a chance to run so you must not only mock that object whenever you need to test something that uses it, but also modify the mock object in the same manner as your start method every single time. Oh, Joe updated the start method, so now that means I have 57 tests I need to update before the build will pass. This is buredensome and will probably result in no tests being written on anything that relies on the object with the heavy constructor.

Because you wanted to call a start method in the constructor.

Jeff Lambert
  • 559
  • 2
  • 9
  • As to your first point, I'm having a tough time imagining a reasonable scenario where object construction and the start must necessarily be distant to each other when the start() method is cheap and virtually always going to succeed. As for your second point, that sword hangs over every object no matter how it is designed. – whatsisname Oct 21 '16 at 02:06
  • Who says it is cheap and always going to suceed? That is an assumption on your part. And just because it is today doesn't mean it will be tomorrow. And this is all about _constructing_ an object. Simply constructing it _should be simple_ and should not be a sword hanging over any object, that is a main point to this. – Jeff Lambert Oct 21 '16 at 03:51
  • 1
    For various situations of "it", instantiation will be cheap. For the example of file operations, creating a new file is cheap and will usually succeed. You claim that constructing "should be simple" as an absolute statement, but it isn't true, there are situations where a little additional complexity in a constructor results in lot less complexity elsewhere, and the trade-off can be worth it. As for the sword allusion you totally missed the point. – whatsisname Oct 21 '16 at 04:15
  • The Misko Hevery article is far more nuanced than you present it. It argues strongly against `init()` (and by extension, `start()`) methods. The article recommends to separate the configuration of something from the instantiation of that something, whereas you advocate objects that contain all config until they are really instantiated by calling `start()`. Similarly, the Code Whisperer article does emphatically not advocate `start()` methods, but extracting dependencies from ctors and doing most of the work *before* the ctor is even invoked – in “named constructors” / static factory methods. – amon Oct 21 '16 at 08:22
  • @amon You are correct, I offered some of the reasons above as to _why_ you should avoid and did not want to recreate the entirity of any of those articles here. I included them as links to promote someone to actually read them, because there's much more information there than I felt was really appropriate to include in a single answer. Whether you perform the work prior to construction or after doesn't really matter in the context of _this question_, as long as you don't do the work _in the constructor_ as the question asked. – Jeff Lambert Oct 21 '16 at 11:25
  • @whatsisname You are right, I am missing your point. _Any_ functionality you put in a constructor _can_ be done elsewhere, and _should_ be, whether it be a factory or whatever. Sure, you may disagree, and call your `start` method from a constructor, only to move it out later when your design runs into problems. I offer here only what I believe is best practice and present some reasons why, in my opinion, I should not ever have to wonder whether my attempt at constructing an object will _ever_ have any other side effect than _constructing an object_. – Jeff Lambert Oct 21 '16 at 11:30
1

TL;DR

In mobile apps, certain objects are created once, but started multiple times - so the placement of code needs to be done carefully to avoid various unwanted effects leading to bad UX.


Having some experience with mobile development, I encounter on a daily basis situations1 where certain objects need to go through a set of initialization, or lifecycle methods before they are truly usable.

To a person making their first app, it might seem completely unnecessary to separate onCreate() from onStart(), and perhaps even from onResume(). Why is this? Probably because the envisioned use case is something like a calculator on your desktop - "open the app, do the things you need, click the big exit button, restart the app at a later time if needed again". Surely, this can work well in some cases, but what happens when you need to take a call while working with the app, or god forbid, try to upload a photo?

If you, the beginner, decided to put everything in onCreate(), when the app is resumed it might be unresponsive or have some unexpected state (paused animations?). If you put everything into onResume(), you might run into some memory leaks because you will end up instantiating objects more than once.

On platforms with relatively limited resources, the developer needs to be very mindful of how and when to use them, because every choice boils down to user experience - is the app responsive? Does it consume a lot of power? Does it require the user to repeat the same actions over and over again?

To conclude: splitting initialization code into several stages (or methods) may improve performance (or at the very least the appearance of performance; by not having to run code a needless amount of times) and overall user experience.


1 - The clearest examples of this are the Android Activitysummary or the iOS UIViewController.

Dev-iL
  • 233
  • 1
  • 10