web-dev-qa-db-fra.com

Méthode recommandée: remplacer __construct () par rapport à la méthode init ()

Lorsque vous sous-classez des objets et souhaitez étendre le code d'initialisation, il existe deux approches. Remplacement de __construct () et implémentation d'une méthode d'initialisation appelée par votre constructeur de superclasse. 

Méthode 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
    }
}

Méthode 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
    }
}

La documentation de Zend Framework semble décourager le remplacement des constructeurs et vous demande de remplacer les méthodes init, le cas échéant, mais cela ne me convient pas du tout. Zend a aussi tendance à faire quelques choses qui ne me plaisent pas, alors je ne suis pas sûr que cela devrait être utilisé comme exemple de meilleure pratique. Personnellement, je pense que la première approche est la bonne, mais j’ai assez souvent vu la seconde approche pour me demander si c’est ce que je devrais faire. 

Avez-vous des commentaires concernant le dépassement de __construct? Je sais que vous devez faire attention à ne pas oublier d'invoquer le constructeur de la super-classe, mais la plupart des programmeurs devraient en être conscients. 

EDIT: Je n'utilise pas Zend, je ne l'utilise que comme exemple de base de code qui vous encourage à utiliser init () au lieu de surcharger __construct (). 

26
GordonM

On dirait que la deuxième approche remet le problème à plus tard.

Si vous avez un cours:

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

    // ...
  }
}

Je choisirais également la première approche, plus logique et plus simple, étant donné que l'appel parent::init() ou parent::__construct() ne pouvait être évité à l'infini. La première approche, IMO, crée moins de confusion.

17
Frosty Z

Les deux seules situations auxquelles je puisse penser dans lesquelles il est logique d'utiliser init() sont celles où votre constructeur n'est pas public mais vous devez donner aux utilisateurs une chance d'influencer l'initialisation, par exemple. dans un singleton abstrait (que vous ne voulez pas utiliser de toute façon). Ou, comme dans Zend Framework, lorsque l’initialisation de supplémentaire _ doit être différée (mais vous n’appelez pas alors init() à partir du constructeur). 

L'appel d'une méthode dans une sous-classe de la super-classe s'appelle Méthode de modèle , d'ailleurs. Le cas d'utilisation serait d'orchestrer un certain flux de travail, mais permettre au sous-type d'influencer des parties de celui-ci. Cela se fait généralement à partir de méthodes habituelles. Notez que votre constructeur ne doit rien orchestrer mais juste initialiser l’objet dans un état valide .

Vous devez absolument not call/offer init() à partir du constructeur pour éviter que les développeurs aient à se rappeler d’appeler le constructeur de supertype. Bien que cela puisse paraître pratique, cela va rapidement perturber la hiérarchie des héritages. Notez également que cela diffère de la façon dont les objets sont généralement initialisés et que les développeurs doivent apprendre ce nouveau comportement, tout comme ils doivent apprendre à appeler le constructeur du supertype.

8
Gordon

D'abord

Zend a aussi tendance à faire quelques petites choses qui ne me plaisent pas

Vous pouvez résoudre ce problème simplement, en ne l'utilisant pas.

Mais deuxièmement, et plus important encore, vous devez remplacer init() et non __construct() car init() fait partie de l'opération d'envoi que Zend utilise. Son utilisation garantit que le reste de votre application est présent et en place. Sinon, cela interrompt le flux du modèle MVC de Zend et peut entraîner un comportement étrange.

Modifier

Je pense que la raison principale pour moi est que cela empêche les autres développeurs de bidouiller. Vous pouvez faire n'importe quoi avec init() mais pas avec __construct() car cela doit fonctionner correctement avec tous les paramètres corrects en place.

Ceci provient des documents Zend:

Bien que vous puissiez toujours remplacer le constructeur du contrôleur d'action, nous Ne le recommandons pas. Zend_Controller_Action :: _ construct () effectue Certaines tâches importantes, telles que l'enregistrement des objets request et response , Ainsi que tout argument d'invocation personnalisé transmis depuis . contrôleur frontal. Si vous devez remplacer le constructeur, assurez-vous d'appeler Parent :: _ construct ($ request, $ response, $ invokeArgs).

3
Jake N

J'utiliserais la fonction init car, si vous remplacez le constructeur, vous devez (normalement) ne pas oublier d'appeler le constructeur parent en haut du constructeur de votre classe enfant. Même si vous en êtes conscient, vous ne pouvez pas garantir qu’un autre développeur chargé de la maintenance de votre application le sera.

1
MW.

Je vois un avantage à utiliser init() au lieu de __construct(): si la signature change dans le constructeur, vous devrez mettre à jour chaque classe dérivée pour qu'elle corresponde à la nouvelle signature.

Si init() n'a pas de paramètre, cela n'arrivera pas.

0
Savageman