Y a-t-il un but pour déclarer une init()
méthode pour un type?
Je ne demande pas si nous devrions préférer init()
un constructeur ou comment éviter de déclarerinit()
.
Je demande s'il y a une raison derrière la déclaration d'une init()
méthode (voir à quel point elle est courante) ou si c'est une odeur de code et devrait être évitée.
L' init()
idiome est assez courant, mais je n'ai encore vu aucun avantage réel.
Je parle de types qui encouragent l'initialisation via une méthode:
class Demo {
public void init() {
//...
}
}
Quand cela sera-t-il utile dans le code de production?
Je pense que cela peut être une odeur de code car cela suggère que le constructeur n'initialise pas complètement l'objet, résultant en un objet partiellement créé. L'objet ne devrait pas exister si son état n'est pas défini.
Cela me fait croire que cela peut faire partie d'une sorte de technique utilisée pour accélérer la production, au sens des applications d'entreprise. C'est la seule raison logique pour laquelle je peux penser à avoir un tel idiome, je ne suis tout simplement pas sûr de savoir comment cela serait bénéfique dans ce cas.
init()
dérivée, ou vice versa?) Si c'est le cas, c'est un exemple de laisser la classe de base exécuter un "post-constructeur "qui ne peut être exécuté qu'après la fin de la construction de la classe la plus dérivée. Il s'agit d'un exemple d'initialisation multiphase.Réponses:
Oui, c'est une odeur de code. Une odeur de code n'est pas quelque chose qui doit nécessairement toujours être supprimée. C'est quelque chose qui vous fait jeter un second coup d'œil.
Ici, vous avez un objet dans deux états fondamentalement différents: pré-init et post-init. Ces États ont des responsabilités différentes, des méthodes différentes qui peuvent être appelées et des comportements différents. Il s'agit en fait de deux classes différentes.
Si vous en faites physiquement deux classes distinctes, vous supprimerez statiquement toute une classe de bugs potentiels, au prix de rendre votre modèle non plus identique au "modèle du monde réel". Vous nommez généralement le premier
Config
ouSetup
quelque chose comme ça.La prochaine fois, essayez de refactoriser vos idiomes construct-init en modèles à deux classes et voyez comment cela se passe pour vous.
la source
this
au constructeur est une odeur de code et pourrait entraîner un code sujet aux erreurs, et il est recommandé de l’éviter quel que soit le domaine auquel appartient votre projet).Ça dépend.
Une
init
méthode est une odeur de code lorsqu'il n'est pas nécessaire de séparer l'initialisation de l'objet du constructeur. Il y a parfois des cas où il est logique de séparer ces étapes.Une recherche rapide sur Google m'a donné cet exemple. Je peux facilement imaginer plus de cas où le code exécuté pendant l'allocation d'objet (le constructeur) pourrait mieux être séparé de l'initialisation elle-même. Peut-être que vous avez un système nivelé, et l'allocation / construction a lieu au niveau X, mais l'initialisation uniquement au niveau Y, car seul Y peut fournir les paramètres nécessaires. Peut-être que le "init" est coûteux et doit être exécuté uniquement pour un sous-ensemble des objets alloués, et la détermination de ce sous-ensemble ne peut être effectuée qu'au niveau Y. Ou vous souhaitez remplacer la méthode (virtuelle) "init" dans un dérivé classe qui ne peut pas être faite avec un constructeur. Peut-être que le niveau X vous fournit des objets alloués à partir d'un arbre d'héritage, mais le niveau Y n'est pas au courant de la dérivation concrète, seulement de l'interface commune (où
init
peut-être défini).Bien sûr, d'après mon expérience, ces cas ne sont qu'un petit pourcentage du cas standard où toute l'initialisation peut être effectuée directement dans le constructeur, et chaque fois que vous voyez une
init
méthode distincte , ce pourrait être une bonne idée de remettre en question sa nécessité.la source
init
méthode distincte . Cependant, chaque fois que vous voyez une telle méthode, n'hésitez pas à remettre en question sa nécessité.init()
méthode, je suis sûr que je gagnerais à en apprendre davantage sur son objectif. Excusez mon ignorance, je suis juste étonné de voir à quel point j'ai du mal à trouver une utilisation, m'empêchant de considérer cela comme quelque chose à éviterMon expérience se décompose en deux groupes:
Dans mon expérience personnelle, je n'ai vu que quelques exemples de (1) mais de nombreux autres exemples de (2). Par conséquent, je suppose généralement qu'un init () est une odeur de code, mais ce n'est pas toujours le cas. Parfois, vous ne pouvez tout simplement pas vous en sortir.
J'ai trouvé que l'utilisation du modèle de générateur peut souvent aider à supprimer le besoin / désir d'avoir un init ().
la source
init()
méthode le résoudrait-il? Lainit()
méthode aurait besoin de paramètres pour accepter les dépendances, ou vous devriez instancier les dépendances dans lainit()
méthode, ce que vous pourriez également faire avec un constructeur. Pouvez-vous donner un exemple?Un scénario typique lorsqu'une méthode Init est utile est lorsque vous avez un fichier de configuration que vous souhaitez modifier et que la modification est prise en compte sans redémarrer l'application. Bien entendu, cela ne signifie pas qu'une méthode Init doit être appelée séparément d'un constructeur. Vous pouvez appeler une méthode Init à partir d'un constructeur, puis l'appeler plus tard lorsque / si les paramètres de configuration changent.
Pour résumer: comme pour la plupart des dilemmes, que ce soit une odeur de code ou non, cela dépend de la situation et des circonstances.
la source
Config
?update
/reload
serait probablement plus descriptif pour ce type de comportement) pour réellement enregistrer ces changements . Dans ce cas, cette notification entraînerait un changement en interne des valeurs de la configuration dans votre application, ce qui, je crois, pourrait être observé en faisant en sorte que votre configuration soit observable, informant les observateurs lorsque la configuration est invitée à modifier l'une de ses valeurs. Ou est-ce que je comprends mal votre exemple?Cela dépend de la façon dont vous les utilisez.
J'utilise ce modèle dans des langages récupérés comme Java / C # lorsque je ne veux pas continuer à réallouer un objet sur le tas (comme lorsque je crée un jeu vidéo et que je dois maintenir des performances élevées, les récupérateurs tuent les performances). J'utilise le constructeur pour faire d'autres allocations de tas dont il a besoin et
init
pour créer l'état utile de base juste avant chaque fois que je veux le réutiliser. Ceci est lié au concept de pools d'objets.Il est également utile si vous avez plusieurs constructeurs qui partagent un sous-ensemble commun d'instructions d'initialisation, mais dans ce cas,
init
ils seront privés. De cette façon, je peux minimiser chaque constructeur autant que possible, donc chacun ne contient que ses instructions uniques et un seul appelinit
pour faire le reste.En général cependant, c'est une odeur de code.
la source
reset()
méthode ne serait-elle pas plus descriptive pour votre première déclaration? Quant au second (plusieurs constructeurs), avoir plusieurs constructeurs est une odeur de code. Il suppose que l'objet a plusieurs objectifs / responsabilités, suggérant une violation de SRP. Un objet doit avoir une seule responsabilité et le constructeur doit définir les dépendances requises pour cette seule responsabilité. Si vous avez plusieurs constructeurs en raison de valeurs facultatives, ils devraient se télescoper (ce qui est également une odeur de code, devrait plutôt utiliser un générateur).string
liste des constructeurs de n'importe quelle langue , des tonnes d'options. Pour moi, il s'agit généralement de 3 constructeurs maximum, mais le sous-ensemble commun d'instructions en tant qu'initialisation a du sens quand elles partagent un code mais diffèrent de quelque manière que ce soit.String
, cela pourrait être résolu en découplant la création de chaînes. En fin de compte, aString
est unString
, et son constructeur ne doit accepter que ce qui est nécessaire pour qu'il fonctionne selon les besoins. La plupart de ces constructeurs sont exposés à des fins de conversion, ce qui constitue une mauvaise utilisation des constructeurs. Les constructeurs ne doivent pas exécuter la logique, sinon ils risquent d'échouer l'initialisation, vous laissant avec un objet inutile.init()
Les méthodes peuvent avoir un certain sens lorsque vous avez des objets qui nécessitent des ressources externes (comme, par exemple, une connexion réseau) qui sont utilisées simultanément par d'autres objets. Vous pourriez ne pas vouloir / avoir besoin de monopoliser la ressource pendant la durée de vie de l'objet. Dans de telles situations, vous pouvez ne pas vouloir allouer la ressource dans le constructeur lorsque l'allocation de ressource est susceptible d'échouer.En particulier dans la programmation intégrée, vous voulez avoir une empreinte mémoire déterministe, il est donc courant (bon?) D'appeler vos constructeurs tôt, peut-être même statiques, et de ne s'initialiser que plus tard lorsque certaines conditions sont remplies.
À part de tels cas, je pense que tout devrait aller dans un constructeur.
la source
init
méthodes est trop restrictif, à mon humble avis.En général, je préfère un constructeur qui reçoit tous les arguments requis pour une instance fonctionnelle. Cela met en évidence toutes les dépendances de cet objet.
D'autre part, j'utilise un cadre de configuration simple qui nécessite un constructeur public sans paramètre et des interfaces pour injecter des dépendances et des valeurs de configuration. Après cela, le cadre de configuration appelle la
init
méthode de l'objet: maintenant vous avez reçu tout ce que j'ai pour vous, faites les dernières étapes pour vous préparer au travail. Mais attention: c'est le framework de configuration qui appelle automatiquement la méthode init, vous n'oublierez donc pas de l'appeler.la source
Il n'y a pas d'odeur de code si la méthode init () - est sémantiquement intégrée dans le cycle de vie d'état de l'objet.
Si vous devez appeler init () pour mettre l'objet dans un état cohérent, c'est une odeur de code.
Il existe plusieurs raisons techniques pour lesquelles une telle structure existe:
la source
Le nom init peut parfois être opaque. Prenons une voiture et un moteur. Pour démarrer la voiture (uniquement sous tension, pour écouter la radio), vous voulez vérifier que tous les systèmes sont prêts à fonctionner.
Vous construisez donc un moteur, une porte, une roue, etc. votre écran affiche le moteur = éteint.
Pas besoin de commencer à surveiller le moteur, etc. car ceux-ci sont tous chers. Ensuite, lorsque vous tournez la clé pour allumer, vous appelez moteur-> démarrer. Il commence à exécuter tous les processus coûteux.
Vous voyez maintenant engine = on. Et le processus d'allumage démarre.
La voiture ne démarre pas sans que le moteur soit disponible.
Vous pouvez remplacer le moteur par votre calcul complexe. Comme une cellule Excel. Toutes les cellules n'ont pas besoin d'être actives avec tous les gestionnaires d'événements tout le temps. Lorsque vous vous concentrez sur une cellule, vous pouvez la démarrer. Augmenter ainsi les performances.
la source