问题
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().
回答1:
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.
回答2:
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.
回答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).
回答4:
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.
回答5:
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.
来源:https://stackoverflow.com/questions/8210561/best-practice-overriding-construct-versus-providing-init-method