J'ai expliqué à un collègue pourquoi un constructeur appelant une méthode peut être un contre-modèle.
exemple (dans mon C ++ rouillé)
class C {
public :
C(int foo);
void setFoo(int foo);
private:
int foo;
}
C::C(int foo) {
setFoo(foo);
}
void C::setFoo(int foo) {
this->foo = foo
}
Je voudrais mieux motiver ce fait grâce à votre contribution supplémentaire. Si vous avez des exemples, des références de livres, des pages de blog ou des noms de principes, ils seraient les bienvenus.
Edit: je parle en général, mais nous codons en python.
this
à l'une des méthodes que vous appelez à partir du constructeur.Réponses:
Vous n'avez pas spécifié de langue.
En C ++, un constructeur doit se méfier lors de l'appel d'une fonction virtuelle, dans la mesure où la fonction réelle qu'il appelle est l'implémentation de classe. S'il s'agit d'une méthode virtuelle pure sans implémentation, ce sera une violation d'accès.
Un constructeur peut appeler des fonctions non virtuelles.
Si votre langage est Java où les fonctions sont généralement virtuelles par défaut, il est logique que vous deviez faire très attention.
C # semble gérer la situation comme vous vous y attendez: vous pouvez appeler des méthodes virtuelles dans les constructeurs et il appelle la version la plus finale. Donc en C # pas un anti-pattern.
Une raison courante pour appeler des méthodes à partir de constructeurs est que plusieurs constructeurs souhaitent appeler une méthode "init" commune.
Notez que les destructeurs auront le même problème avec les méthodes virtuelles, donc vous ne pouvez pas avoir une méthode de "nettoyage" virtuelle qui se trouve à l'extérieur de votre destructeur et vous attendre à ce qu'elle soit appelée par le destructeur de classe de base.
Java et C # n'ont pas de destructeurs, ils ont des finaliseurs. Je ne connais pas le comportement avec Java.
C # semble gérer correctement le nettoyage à cet égard.
(Notez que bien que Java et C # aient une récupération de place, cela ne gère que l'allocation de mémoire. Il y a un autre nettoyage dont votre destructeur a besoin pour faire qui ne libère pas de mémoire).
la source
OK, maintenant que la confusion concernant les méthodes de classe vs les méthodes d' instance est éliminée, je peux donner une réponse :-)
Le problème n'est pas avec l'appel des méthodes d'instance en général à partir d'un constructeur; c'est avec l'appel de méthodes virtuelles (directement ou indirectement). Et la raison principale est que tandis qu'à l'intérieur du constructeur, l'objet n'est pas encore entièrement construit . Et surtout ses parties de sous-classe ne sont pas du tout construites pendant que le constructeur de la classe de base s'exécute. Son état interne est donc incohérent d'une manière dépendante de la langue, ce qui peut provoquer différents bogues subtils dans différentes langues.
C ++ et C # ont déjà été discutés par d'autres. En Java, la méthode virtuelle du type le plus dérivé sera appelée, mais ce type n'est pas encore initialisé. Donc, si cette méthode utilise des champs du type dérivé, ces champs peuvent ne pas encore être initialisés correctement à ce moment. Ce problème est abordé en détail dans Effecive Java 2nd Edition , Item 17: Design and document for inheritance or else prohibit it .
Notez qu'il s'agit d'un cas particulier du problème général de publication prématurée des références d'objets . Les méthodes d'instance ont un
this
paramètre implicite , mais le passagethis
explicite à une méthode peut provoquer des problèmes similaires. Surtout dans les programmes concurrents où si la référence d'objet est publiée prématurément sur un autre thread, ce thread peut déjà appeler des méthodes dessus avant que le constructeur du premier thread ne se termine.la source
Je ne considérerais pas les appels de méthode ici comme un contre-motif en soi, plutôt comme une odeur de code. Si une classe fournit une
reset
méthode, qui renvoie un objet à son état d'origine, alors appelerreset()
le constructeur est DRY. (Je ne fais aucune déclaration sur les méthodes de réinitialisation).Voici un article qui pourrait aider à satisfaire votre appel à l'autorité: http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/
Il ne s'agit pas vraiment d'appeler des méthodes, mais de constructeurs qui en font trop. À mon humble avis, appeler des méthodes dans un constructeur est une odeur qui pourrait indiquer qu'un constructeur est trop lourd.
Cela est lié à la facilité avec laquelle il est possible de tester votre code. Les raisons incluent:
Les tests unitaires impliquent beaucoup de création et de destruction - la construction doit donc être rapide.
Selon ce que font ces méthodes, il peut être difficile de tester des unités de code discrètes sans s'appuyer sur une condition préalable (potentiellement non testable) configurée dans le constructeur (par exemple, obtenir des informations d'un réseau).
la source
Philosophiquement, le but du constructeur est de transformer un morceau brut de mémoire en une instance. Pendant l'exécution du constructeur, l'objet n'existe pas encore, donc appeler ses méthodes est une mauvaise idée. Vous ne savez peut-être pas ce qu'ils font en interne après tout, et ils peuvent à juste titre considérer que l'objet existe au moins (duh!) Lorsqu'ils sont appelés.
Techniquement, il n'y a peut-être rien de mal à cela, en C ++ et surtout en Python, c'est à vous de faire attention.
En pratique, vous devez limiter les appels aux seules méthodes qui initialisent les membres de la classe.
la source
Ce n'est pas une question d'ordre général. C'est un problème en C ++, en particulier lors de l'utilisation de l'héritage et des méthodes virtuelles, car la construction d'objet se fait à l'envers, et les pointeurs vtable sont réinitialisés avec chaque couche constructeur dans la hiérarchie d'héritage, donc si vous appelez une méthode virtuelle, vous pourriez ne pas finissent par obtenir celui qui correspond réellement à la classe que vous essayez de créer, ce qui va à l'encontre du but de l'utilisation de méthodes virtuelles.
Dans les langues avec une prise en charge OOP saine, qui définissent correctement le pointeur vtable dès le début, ce problème n'existe pas.
la source
Il y a deux problèmes avec l'appel d'une méthode:
Il n'y a rien de mal à appeler une fonction d'assistance, tant qu'elle ne tombe pas dans les deux cas précédents.
la source
Je n'achète pas ça. Dans un système orienté objet, appeler une méthode est à peu près la seule chose que vous pouvez faire. En fait, c'est plus ou moins la définition de "orienté objet". Donc, si un constructeur ne peut appeler aucune méthode, que peut- il faire?
la source
Dans la théorie de la POO, cela ne devrait pas avoir d'importance, mais dans la pratique, chaque langage de programmation POO gère des constructeurs différents . Je n'utilise pas très souvent des méthodes statiques.
En C ++ et Delphi, si je devais donner des valeurs initiales à certaines propriétés ("membres de champ"), et que le code est très étendu, j'ajoute quelques méthodes secondaires comme extension des constructeurs.
Et n'appelez pas d'autres méthodes qui font des choses plus complexes.
En ce qui concerne les méthodes "getters" et "setters" des propriétés, j'utilise généralement des variables privées / protégées pour stocker leur état, ainsi que les méthodes "getters" & "setters".
Dans le constructeur, j'attribue des valeurs "par défaut" aux champs d'état des propriétés, SANS appeler les "accesseurs".
la source