28

When you are subclassing objects and want to extend the initialization code, there are two approaches. Overriding __construct(), and implementing an initialization method that your superclass constructor calls.

Method 1:

class foo
{
    public function __construct ($arg1, $arg2, $arg3)
    {
        // Do initialization
    }
}

class bar extends foo
{
    public function __construct ($arg1, $arg2, $arg3)
    {
        parent::__construct ($arg1, $arg2, $arg3);
        // Do subclass initialization
    }
}

Method 2

class foo
{
    public function init ()
    {
        // Dummy function
    }

    public function __construct ($arg1, $arg2, $arg3)
    {
        // Do subclass defined initialization
        $this -> init ();
        // Do other initialization
    }
}

class bar extends foo
{
    public function init ()
    {
        // Do subclass initialization
    }
}

The documentation for Zend Framework seems to discourage overriding constructors and wants you to override init methods, where provided, but this somehow just doesn't feel right to me. Zend also tends to do a few things that I'm not happy with so I'm not sure if it should be used as an example of best practice. I personally think the first approach is the correct one but I've seen the second approach often enough to wonder if that's actually what I should be doing.

Do you have any comments regarding overriding __construct? I know you have to be careful to remember to invoke the superclass constructor, but most programmers should be aware of that.

EDIT: I'm not using Zend, I'm only using it as an example of a codebase that encourages you to use init() instead of overriding __construct().

Jake N
  • 10,535
  • 11
  • 66
  • 112
GordonM
  • 31,179
  • 15
  • 87
  • 129

5 Answers5

18

Looks like the second approach is postponing the problem.

If you have a class:

class bar2 extends bar // which already extends foo
{
  public function init()
  {
    // You should then do anyway:
    parent::init();

    // ...
  }
}

I would go for the first approach too, more logical and straightforward, since the parent::init() or parent::__construct() call could not be endlessly avoided. The first approach, IMO, is less confusing.

Maxime Pacary
  • 22,336
  • 11
  • 85
  • 113
8

The only two situations I can think of in which it makes sense to use init() is when your constructor is non-public but you need to give people a chance to influence initialization, e.g. in an abstract Singleton (which you do not want to use anyway). Or, like in Zend Framework, when additional initialization should be defered (but then you don't call init() from the constructor).

Calling a method in a subclass from the superclass is called Template Method, by the way. The UseCase would be to orchestrate a certain workflow but allow the subtype to influence parts of it. This is usually done from regular methods though. Note that your constructor should not orchestrate anything but just initialize the object into a valid state.

You definitely should not call/offer init() from the constructor to prevent developers having to remember to call the supertype constructor. While that may sound convenient, it will quickly mess up the inheritance hierarchy. Also note that it deviates from how objects are usually initialized and developers have to learn this new behavior just like they have to learn to call the supertype's constructor.

marcv
  • 1,874
  • 4
  • 24
  • 45
Gordon
  • 312,688
  • 75
  • 539
  • 559
  • I wouldn't want to be using singletons and registries and the like, I get enough of that at work and it doesn't make my life any easier :) The deferred initialization solution looks like it's probably the only real valid use for this approach to me, and even then I get the feeling it should be used sparingly. Thanks for the pointers – GordonM Nov 21 '11 at 19:26
  • BTW, the other guy's answer got accepted because I suspect you don't really need the rep at this point. :) – GordonM Nov 21 '11 at 19:28
  • @GordonM meh, and here I was thinking the most helpful answer gets the green tick :P – Gordon Nov 22 '11 at 10:47
  • Oh come, you should know it's all about cliques on the internet by now. :) Seriously, the answer I accepted made a good point. Your points were also good (so upvote) but will require a bit of free tiem to read through all the links. – GordonM Nov 22 '11 at 11:28
3

Firstly

Zend also tends to do a few things that I'm not happy with

You can solve this simply, by not using it.

But secondly and more importantly you should override init() and not __construct() because init() is part of the dispatch operation that Zend uses and using it ensures that the rest of your App is there and in place. Doing otherwise breaks the flow of Zend's MVC model and may result in odd behaviour.

Edit

I think the main reason for me is that it stops other developers from fiddling. You can do anything with init() but not with __construct() as this needs to run correctly with all the correct params in place.

This is from the Zend Docs:

While you can always override the action controller's constructor, we do not recommend this. Zend_Controller_Action::_construct() performs some important tasks, such as registering the request and response objects, as well as any custom invocation arguments passed in from the front controller. If you must override the constructor, be sure to call parent::_construct($request, $response, $invokeArgs).

Jake N
  • 10,535
  • 11
  • 66
  • 112
  • Sorry, I should have been a bit more clear in my question. I'm not using Zend, I'm working on code that I'm planning to release to the public at some point in the future if I feel it might be useful. I was just using Zend as an example, as it tends to use the init method, and if a major framework is doing that then there must be a reason. (even if I don't personally like the framework in question much) – GordonM Nov 21 '11 at 11:28
  • Ah right. Might be worth editing and putting that in your question. I have edited by answer. – Jake N Nov 21 '11 at 11:57
1

I would use the init function because if you override the constructor you (normally) have to remember to call the parent constructor at the top of your child class's constructor. While you may be aware of that, you can not guarantee that another developer tasked with maintining your application will be.

MW.
  • 12,550
  • 9
  • 36
  • 65
0

I can see one benefit from using init() instead of __construct() : if the signature changes in the constructor, you will have to update every derived class to match the new signature.

If init() has no parameter, this won't happen.

Savageman
  • 9,257
  • 6
  • 40
  • 50
  • 1
    Wouldn't that also be true if you have a class with an init() implementation that got subclassed? Wouldn't the subclasses also have to be updated to reflect changes in the superclass init method? – GordonM Nov 21 '11 at 19:24