views:

137

answers:

3

Instead of using an interface like this:

public interface IStartable
{
    void Start();
    void Stop();
}

I usually just make the constructor of an object run the Start() code, and implement IDisposable so that the dispose method runs the Stop() code.

Is it just a matter of style? Or am I missing something important by not having something like IStartable? All I see is extra complexity, because you have to maintain it's started/stopped state.

What are the pros and cons of using start/stop vs using ctor/dispose, especially in the context of an IoC/DI container?

EDIT: Great answers, you've convinced me to use an interface for startable objects. I can't decide who's answer is the best so I'll accept whoever has the most up votes after 24 hours.

+3  A: 

The general advantage to using an interface is that they're self-describing and self-advertising. If there's no interface, you don't have a way to ask an object, "can you be started and stopped?" If you do use an interface, by contrast, you can query objects to see which of them will respond to those kinds of messages. Then you can be safely guaranteed that such objects have implemented the functionality encapsulated by the interface.

John Feminella
This is indeed a big pro, how do you manage the complexity that it adds? Do you bother worrying about multiple starts, or being able to start again after the object has been stopped?
Jacob Stanley
I wouldn't say it necessarily adds any complexity. After all, you already had to write an implementation one way or another. Now you're simply declaring that it's conforming to some external interface. As for your second question, the interface would have to define the rules about that. Conforming implementations are expected to meet whatever standards are described. For example, if you're IEquatable<T>, your Equals implementation must guarantee that a comparison to a null object always yields false.
John Feminella
But one thing that "starting" in the constructor buys you is that the start method can't be called twice, and you don't have to worry about the possibility of re-starting. Is it acceptable to say that an interface has "undefined" behaviour for multiple starts? or re-starts? Or would it be better to have some helpers that throw exceptions for invalid state changes so that only Initialized -> Started -> Stopped is allowed, and other transitions will throw?
Jacob Stanley
Why not just require Start implementations to be idempotent, like a car ignition? If you're already started, nothing else happens until you Stop again.
John Feminella
Would you say that if you stop an object then you're allowed to start it again, or would it depend on the object/contract?
Jacob Stanley
Like I said, the interface gets to define the rules; it's up to implementers to write conforming implementations. Nothing stops an ill-behaved implementation from doing nothing except throwing NotImplementedException() when invoke its Start method, for example. In this case, I don't see anything wrong with encouraging implementers to allow restarts after a Stop().
John Feminella
+3  A: 

in general, constructors should produce a properly-initialized object

and nothing more!

Steven A. Lowe
Couldn't "properly initialized" mean started in this case? Or are you saying that constructors shouldn't have side-effects? If you were to run with this idea, would you say that even subscribing to events is too much?
Jacob Stanley
constructors should not have side-effects, they should not perform extensive calculations, they should not do anything but allocate and initialize memory. I definitely would not subscribe to events. Part of the problem (in .NET at least) is that the unsubscribe (or Stop) would then logically belong in the dispose or finalize method, and they do not always get called as soon as you think they would
Steven A. Lowe
plus interfaces make the protocols more explicit, like John said
Steven A. Lowe
+1  A: 

It could possibly depend on what, specifically, you mean to be happening when you say Start(). But in general, mixing object initialization with routine execution (especially stateful and/or long-running execution!) violates SoC.

It also leaves a great deal of ambiguity. To a consumer, for a given object how do we know it is "starting" when we invoke the ctor? "For this given object, which implements no contract, I must leave it to hope in the author that it conforms to my expectations"? An interface makes the presence and availability of the action explicit.

Rex M