25

Let's say in some reason all objects are created this way $obj = CLASS::getInstance(). Then we inject dependencies using setters and perform starting initialization using $obj->initInstance(); Are there any real troubles or situations, which can't be solved, if we won't use constructors at all?

P.s. the reason to create object this way, is that we can replace class inside getInstance() according to some rules.

I'm working in PHP, if that matter

8
  • what programming language?
    – gnat
    Jan 13, 2014 at 14:23
  • 2
    PHP (why does it matter?) Jan 13, 2014 at 14:25
  • 8
    Looks like that what you want to do can be achieved using the Factory pattern.
    – superM
    Jan 13, 2014 at 14:30
  • 1
    Just as a note: the factory patern @superM mentioned is one way to implement Inversion of Control (Dependency Injection) is another one. Jan 13, 2014 at 15:33
  • Isn't that roughly what javascript does with objects?
    – Thijser
    Jan 13, 2014 at 22:07

10 Answers 10

44

I'd say that this severly hinders your design space.

Constructors are a great place to initialize and validate parameters passed in. If you can no longer use them for that, then initialization, state handling (or plain out denying constructor of "broken" objects) becomes a lot harder and partially impossible.

For example, if every Foo object needs a Frobnicator then it might check in its constructor if the Frobnicator is non-null. If you take away the constructor, then it gets harder to check. Do you check at every point where it would be used? In an init() method (effectively externalizing the constructor method)? Never check it and hope for the best?

While you could probably still implement everything (after all, you're still turing complete), some things will be a lot harder to do.

Personally I'd suggest looking into Dependency Injection/Inversion of Control. These techniques also allow switching concrete implementation classes, but they don't prevent writing/using constructors.

3
  • What did you mean by "or plain out denying constructor of "broken" objects"?
    – Geek
    Jan 13, 2014 at 17:33
  • 2
    @Geek: a constructor can inspect its argument and decide if those would result in a working object (for example if your object needs a HttpClient then it checks if that parameter is non-null). And if those constraints are not met, then it can throw an exception. That's not really possible with the construct-and-set-values approach. Jan 13, 2014 at 18:17
  • 1
    I think OP was just describing externalization of the constructor inside init(), which is entirely possible—although this just imposes more of a maintenance burden.
    – Peter
    Jan 13, 2014 at 23:32
26

2 advantages to constructors:

Constructors allow for the an object's construction steps to be done atomically.

I could avoid a constructor and use setters for everything, but what about mandatory properties as Joachim Sauer suggested? With a constructor, an object owns its own construction logic so as to ensure that there are no invalid instances of such class.

If creating an instance of Foo requires 3 properties to be set, the constructor could take a reference to all 3, validate them and throw an exception if they are invalid.

Encapsulation

By relying solely on setters, the burden is on the consumer of an object to build it properly. There could be different combinations of properties that are valid.

For example, every Foo instance needs either an instance of Bar as property bar or an instance of BarFinder as property barFinder. It can use either one. You can create a constructor for every valid set of parameters and enforce the conventions that way.

The logic and semantics for the objects live within the object itself. It's good encapsulation.

15

Yes, you can live without constructors.

Sure, you may end up with a lot of duplicated boiler plate code. And if your application is of any scale, you'll likely spend a lot of time trying to locate the source of a problem when that boilerplate code isnt used consistently across the application.

But no, you don't strictly 'need' your own constructors. Of course, you don't strictly 'need' classes and objects, either.

Now if your goal is to use some sort of factory pattern for object creation, that isnt mutually exclusive to using constructors when initializing your objects.

2
  • Absolutely. One can even live without classes and objects, to begin with.
    – JensG
    Jan 13, 2014 at 21:58
  • 5
    All hail the mighty assembler! Jan 14, 2014 at 3:58
9

The advantage of using constructors is that they make it easier to ensure that you will never have an invalid object.

A constructor gives you the opportunity to set all member variables of the object to a valid state. When you then ensure that no mutator method can change the object to an invalid state, you will never have an invalid object, which will save you from a lot of bugs.

But when a new object is created in an invalid state and you must call some setters to put it into a valid state in which it can be used, you are risking that a consumer of the class forgets to call these setters or calls them incorrectly and you end up with an invalid object.

A workaround could be to create objects only through a factory-method which checks every object it creates for validity before returning it to the caller.

3

$obj = CLASS::getInstance(). Then we inject dependencies using setters and perform starting initialization using $obj->initInstance();

I think you're making this more difficult than it needs to be. We can inject dependencies just fine through the constructor - and if you have a lot of them, just use a dictionary-like structure so you can specify what ones you want to use:

$obj = new CLASS(array(
    'Frobnicator' => (),
    'Foonicator' => (),
));

And within the constructor, you can ensure consistency like so:

if (!array_key_exists('Frobnicator', $args)) {
    throw new Exception('Frobnicator required');
}
if (!array_key_exists('Foonicator', $args)) {
    $args['Foonicator'] = new DefaultFoonicator();
}

$args can then be used to set private members as necessary.

When done entirely within the constructor like so, there will never be an intermediate state where $obj exists but is not initialized, as there would be in the system described in the question. It's better to avoid such intermediate states, because you can't guarantee the object is always going to be used correctly.

2

I was actually thinking about similar things.

The question I asked was "What constructor does, and is it possible to do it differently?" I reached those conclusions:

  • It ensures some properties are initialized. By accepting them as parameters and setting them. But this could easily be enforced by compiler. By simply annotating the fields or properties as "required" the compiler would check during creation of the instance if everything is properly set. The call to create the instance would probably be same, there just wouldn't be any constructor method.

  • Ensures the properties are valid. This could be easily achieved by assert condition. Again you would just annotate the properties with correct conditions. Some languages already do this.

  • Some more complex construction logic. Modern patterns don't recommend to do that in constructor, but propose using specialized factory methods or classes. So the use of constructors in this case is minimal.

So to answer your question: Yes, I believe it is possible. But it would require some big changes in language design.

And I just noticed my answer is pretty OT.

2

Yes, you can do nearly everything without using constructors but this is clearly squandering benefits of Object-Oriented Programming languages.

In modern languages (I'll talk here about C# which I program in) you can limit parts of code that can be run only in a constructor. Thanks to which you can avoid clumsy mistakes. One of such a thing is readonly modifier:

public class A {
    readonly string rostring;

    public A(string arg) {
        rostring = arg;
    }

    public static A CreateInstance(string arg) {
        var result = new A();
        A.rostring = arg;  // < because of this the code won't compile!
        return result;
    }
}

As recommended by Joachim Sauer previously instead of using Factory design patter read about Dependency Injection. I would recommend reading Dependency Injection in .NET by Mark Seemann.

1

Instantiate an object with a type depending on the requirements it is absolutely possible. It could be the object itself, using global variables of the system to return a specific type.

However, have a class in the code that can be "All" is the concept of dynamic type. I personally believe this approach create inconsistency in your code, make the test complexes*, and "the future becomes uncertain" with respect to the flow of the proposed work.

*I am referring to the fact that the tests must consider first the type, second the result you want to achieve. Then you are creating large nested test.

1

To balance some of the other answers, claiming:

A constructor gives you the opportunity to set all member variables of the object to a valid state... you will never have an invalid object, which will save you from a lot of bugs.

and

With a constructor, an object owns its own construction logic so as to ensure that there are no invalid instances of such class.

Such statements sometimes imply the assumption that:

If an class has a constructor that, by the time it exits, has put the object into a valid state, and none of the class's methods mutate that state to make it invalid, then it is impossible for code outside the class to detect an object of that class in an invalid state.

But this is not quite true. Most languages do not have a rule against a constructor passing this (or self or whatever the language calls it) to external code. Such a constructor completely abides by the rule stated above, and yet it risks exposing semi-constructed objects to external code. It's a minor point but easily overlooked.

0

This is somewhat anecdotal, but I usually reserve constructors for state essential for the object to be complete and usable. Barring any setter-injection, once the constructor is run, my object should be able to perform the tasks required of it.

Anything that can be deferred, I leave out of the constructor (preparing output values, etc). With this approach, I feel not using constructors for anything but dependency injection makes sense.

This also has the added benefit of hard-wiring your mental design process to not do anything prematurely. You won't initialize or perform logic that may never end up being used because at best all you're doing is basic setup for the work ahead.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.