web-dev-qa-db-fra.com

Les méthodes init () sont-elles une odeur de code?

Est-il utile de déclarer une méthode init() pour un type?

Je ne demande pas si nous devons préférer init() à un constructeur ou comment éviter de déclarer init() .

Je demande s'il y a une justification derrière la déclaration d'une méthode init() (voir à quel point elle est courante) ou si c'est un code odeur et doit être évité.


L'idiome init() 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, dans le 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.

20
Vince Emigh

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 bogues potentiels, au prix de faire en sorte que votre modèle ne corresponde pas aussi étroitement au "modèle du monde réel". Vous nommez généralement le premier Config ou Setup ou 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.

40
Karl Bielefeldt

Ça dépend.

Une méthode init 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 voulez 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'une arborescence d'héritage, mais le niveau Y n'est pas conscient de la dérivation concrète, uniquement de l'interface commune (où init peut être défini).

Bien sûr, d'après mon expérience, ces cas ne représentent 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 méthode init distincte, ce pourrait être une bonne idée de remettre en question sa nécessité.

14
Doc Brown

Mon expérience se décompose en deux groupes:

  1. Code où init () est réellement requis. Cela peut se produire lorsqu'une superclasse ou un framework empêche le constructeur de votre classe d'obtenir toutes ses dépendances pendant la construction.
  2. Code où init () est utilisé mais aurait pu être évité.

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 Builder Pattern peut souvent aider à supprimer le besoin/désir d'avoir un init ().

6
Ivan

Les méthodes init() 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. Il se peut que vous ne souhaitiez/ayez pas 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.

1
tofro

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. Cela, bien sûr, 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, qu'il s'agisse d'une odeur de code ou non, cela dépend de la situation et des circonstances.

1
Vladimir Stokic

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 garbage collector 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 sera privé. De cette façon, je peux minimiser chaque constructeur autant que possible, donc chacun ne contient que ses instructions uniques et un seul appel à init pour faire le reste.

En général cependant, c'est une odeur de code.

1
Cody

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'un autre côté, 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 méthode init 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.

0
Bernhard Hiller

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:

  1. crochet de cadre
  2. réinitialiser l'objet à son état initial (éviter la redondance)
  3. possibilité de passer outre lors des tests
0
oopexpert