Il m'est souvent difficile de décider laquelle de ces deux manières d'utiliser lorsque j'ai besoin d'utiliser des données communes pour certaines méthodes de mes classes. Quel serait un meilleur choix?
Dans cette option, je peux créer une variable d'instance pour éviter d'avoir à déclarer des variables supplémentaires, ainsi que pour définir les paramètres de la méthode, mais il est peut-être difficile de savoir où ces variables sont instanciées / modifiées:
public class MyClass {
private int var1;
MyClass(){
doSomething();
doSomethingElse();
doMoreStuff();
}
private void doSomething(){
var1 = 2;
}
private void doSomethingElse(){
int var2 = var1 + 1;
}
private void doMoreStuff(){
int var3 = var1 - 1;
}
}
Ou simplement instancier des variables locales et les passer comme arguments?
public class MyClass {
MyClass(){
int var1 = doSomething();
doSomethingElse(var1);
doMoreStuff(var1);
}
private int doSomething(){
int var = 2;
return var;
}
private void doSomethingElse(int var){
int var2 = var + 1;
}
private void doMoreStuff(int var){
int var3 = var - 1;
}
}
Si la réponse est que les deux sont corrects, lequel est le plus souvent vu / utilisé? En outre, si vous pouvez fournir des avantages / inconvénients supplémentaires pour chaque option serait très utile.
java
coding-style
Carlossierra
la source
la source
Réponses:
Je suis surpris que cela n'ait pas encore été mentionné ...
Cela dépend si
var1
fait réellement partie de l' état de votre objet .Vous supposez que ces deux approches sont correctes et qu'il s'agit simplement d'une question de style. Vous avez tort.
C'est entièrement sur la façon de modéliser correctement.
De même, il
private
existe des méthodes d'instance pour muter l'état de votre objet . Si ce n'est pas ce que fait votre méthode, alors elle devrait l'êtreprivate static
.la source
transient
n'a rien à voir avec cela, car iltransient
s'agit d' un état persistant , comme dans certaines parties de l'objet qui sont enregistrées lorsque vous effectuez une opération telle que la sérialisation de l'objet. Par exemple, le magasin de sauvegarde d'un ArrayList esttransient
même crucial pour l'état de ArrayList, car lorsque vous sérialisez un ArrayList, vous ne voulez enregistrer que la partie du magasin de sauvegarde contenant les éléments ArrayList réels, et non l'espace libre. à la fin réservé à d'autres ajouts d'élément.var1
est nécessaire pour quelques méthodes mais ne fait pas partie de l'état deMyClass
, il peut être temps de placervar1
ces méthodes dans une autre classe à utiliser parMyClass
.Je ne sais pas ce qui est le plus répandu, mais je ferais toujours le dernier. Il communique plus clairement le flux de données et la durée de vie, et il ne bloque pas chaque instance de votre classe avec un champ dont la seule durée de vie pertinente est pendant l'initialisation. Je dirais que le premier est source de confusion et rend la révision de code beaucoup plus difficile, car je dois envisager la possibilité que toute méthode puisse être modifiée
var1
.la source
Vous devriez réduire la portée de vos variables autant que possible (et raisonnable). Non seulement dans les méthodes, mais en général.
Pour votre question, cela signifie que cela dépend si la variable fait partie de l'état de l'objet. Si oui, il est correct de l'utiliser dans cette étendue, c'est-à-dire tout l'objet. Dans ce cas, choisissez la première option. Si non, choisissez la deuxième option car elle réduit la visibilité des variables et donc la complexité globale.
la source
Il y a un autre style - utiliser un contexte / état.
Cette approche présente plusieurs avantages. Les objets d'état peuvent changer indépendamment de l'objet, par exemple, ce qui laisse beaucoup de marge de manœuvre pour l'avenir.
C'est un modèle qui fonctionne également bien dans un système distribué / serveur où certains détails doivent être préservés lors d'appels. Vous pouvez stocker les détails de l'utilisateur, les connexions à la base de données, etc. dans l'
state
objet.la source
Il s'agit d'effets secondaires.
Demander si cela
var1
fait partie de l'État passe à côté de l'essentiel de cette question. Bien sûr, sivar1
doit persister, il doit s'agir d'une instance. Quelle que soit l'approche choisie, que la persistance soit nécessaire ou non.L'approche des effets secondaires
Certaines variables d'instance ne sont utilisées que pour communiquer entre méthodes privées d'un appel à l'autre. Ce type de variable d'instance peut être remanié en dehors de l'existence, mais ce n'est pas obligatoire. Parfois, les choses sont plus claires avec eux. Mais ce n'est pas sans risque.
Vous laissez une variable hors de sa portée car elle est utilisée dans deux étendues privées différentes. Pas parce que c'est nécessaire dans le champ d'application que vous placez. Cela peut être déroutant. Les "globals sont diaboliques!" niveau de confusion. Cela peut marcher mais ça ne va pas bien. Cela ne fonctionne que dans le petit. Pas de gros objets. Pas de longues chaînes d'héritage. Ne cause pas d' effet yo yo .
L'approche fonctionnelle
Maintenant, même si
var1
doit persister, rien ne dit que vous devez utiliser si, pour chaque valeur transitoire, elle peut prendre avant d’atteindre l’état que vous souhaitez préserver entre les appels publics. Cela signifie que vous pouvez toujours définir unevar1
instance en utilisant uniquement des méthodes plus fonctionnelles.Donc, qu’il fasse partie de l’État ou non, vous pouvez toujours utiliser l’une ou l’autre approche.
Dans ces exemples, 'var1' n'est donc pas encapsulé, à part que le débogueur le sache. Je suppose que vous avez fait cela délibérément parce que vous ne voulez pas nous biaiser. Heureusement, je m'en fiche.
Le risque d'effets secondaires
Cela dit, je sais d'où vient votre question. J'ai travaillé avec un héritage misérable de votre part qui mute une variable d'instance à plusieurs niveaux en plusieurs méthodes et qui est parti à la louche en essayant de la suivre. C'est le risque.
C'est la douleur qui me pousse vers une approche plus fonctionnelle. Une méthode peut documenter ses dépendances et la sortie dans sa signature. C'est une approche puissante et claire. Cela vous permet également de changer ce que vous passez avec la méthode privée pour la rendre plus réutilisable dans la classe.
L'avantage des effets secondaires
C'est aussi limitant. Les fonctions pures n'ont pas d'effets secondaires. Cela peut être une bonne chose mais ce n’est pas orienté objet. Une grande partie de l'orientation des objets est la possibilité de faire référence à un contexte extérieur à la méthode. Le fait de ne pas laisser échapper de globales partout ici et disparaître est la force de la POO. Je reçois la flexibilité d’un global mais c’est bien contenu dans la classe. Je peux appeler une méthode et muter chaque variable d'instance à la fois si je le souhaite. Si je fais cela, je suis obligé au moins de donner à la méthode un nom qui précise ce que c'est que faire pour que les gens ne soient pas surpris quand cela se produit. Les commentaires peuvent aussi aider. Parfois, ces commentaires sont formalisés en tant que "conditions post".
Les inconvénients des méthodes privées fonctionnelles
L'approche fonctionnelle clarifie certaines dépendances. Sauf dans un langage purement fonctionnel, il ne peut pas exclure les dépendances cachées. Vous ne savez pas, en regardant simplement une signature de méthode, que cela ne vous cache pas un effet secondaire dans le reste de son code. Tu ne le fais pas.
Post conditionals
Si vous et tous les autres membres de l’équipe documentez de manière fiable les effets secondaires (conditions pré / post) dans les commentaires, le gain de l’approche fonctionnelle sera bien moindre. Ouais je sais, rêve sur.
Conclusion
Personnellement, dans les deux cas, j'ai tendance à utiliser des méthodes privées fonctionnelles, mais honnêtement, c'est surtout parce que ces commentaires sur les effets secondaires pré / post conditionnels ne causent pas d'erreurs de compilation lorsqu'ils sont obsolètes ou lorsque des méthodes sont appelées en panne. Sauf si j'ai vraiment besoin de la flexibilité des effets secondaires, je préfère simplement savoir que les choses fonctionnent.
la source
var1
tant que variable d’état en modifiant les paradigmes. La portée de la classe n'est PAS là où l'état est stocké. C'est aussi une portée englobante. Cela signifie qu'il y a deux motivations possibles pour mettre une variable au niveau de la classe. Vous pouvez prétendre le faire uniquement pour le champ d’application englobant et non pour dire que c’est le mal, mais j’affirme que c’est un compromis avec l’ arité qui est également pervers. Je le sais parce que j'ai dû maintenir un code qui le fait. Certains ont été bien fait. Certains étaient un cauchemar. La ligne entre les deux n'est pas l'état. C'est la lisibilité.La première variante semble non intuitive et potentiellement dangereuse pour moi (imaginez, pour une raison quelconque, que quelqu'un rende publique votre méthode privée).
Je préférerais de beaucoup instancier vos variables lors de la construction de la classe ou les transmettre comme arguments. Ce dernier vous donne la possibilité d'utiliser des idiomes fonctionnels et de ne pas vous fier à l'état de l'objet contenant.
la source
Il existe déjà des réponses sur l'état d'objet et sur le choix de la seconde méthode. Je veux juste ajouter un cas d'utilisation courant pour le premier motif.
Le premier modèle est parfaitement valide lorsque votre classe ne fait qu’encapsuler un algorithme . Un cas d’utilisation pour ceci est que si vous écriviez l’algorithme sur une seule méthode, il serait trop volumineux. Donc, vous le divisez en méthodes plus petites, faites-en une classe et rendez les sous-méthodes privées.
Maintenant, passer tous les états de l'algorithme via des paramètres peut devenir fastidieux, vous devez donc utiliser les champs privés. Il est également en accord avec la règle du premier paragraphe car il s’agit essentiellement d’un état de l’instance. Vous devez seulement garder à l'esprit et documenter correctement que l'algorithme n'est pas réentrant si vous utilisez des champs privés pour cela . Cela ne devrait pas être un problème la plupart du temps, mais cela peut éventuellement vous mordre.
la source
Essayons un exemple qui fait quelque chose. Pardonnez-moi, javascript pas java. Le point devrait être le même.
Visitez https://blockly-games.appspot.com/pond-duck?lang=fr , cliquez sur l'onglet javascript et collez-le:
Vous remarquerez que
dir
,wid
etdis
ne se passé autour d' un lot. Vous devriez également remarquer que le code dans la fonction quilock()
retourne ressemble beaucoup à du pseudo-code. Eh bien, c'est le code réel. Pourtant, c'est très facile à lire. Nous pourrions ajouter des passes et des assignations, mais cela ajouterait un fouillis que vous ne verriez jamais dans un pseudo-code.Si vous souhaitez faire valoir que ne pas effectuer l'assignation et le passage est correct car ces trois vars sont persistants, envisagez une refonte qui attribue
dir
une valeur aléatoire à chaque boucle. Pas persistant maintenant, est-ce?Bien sûr, nous pourrions maintenant nous écarter
dir
de la portée, mais non sans être obligés d'encombrer notre pseudo-code, tel que le code, le passage et le réglage.Donc non, l’Etat n’est pas la raison pour laquelle vous décidez d’utiliser ou non des effets secondaires plutôt que de passer et de revenir. Les effets secondaires ne signifient pas non plus que votre code est illisible. Vous n'obtenez pas les avantages du code fonctionnel pur . Mais bien fait, avec de bons noms, ils peuvent en fait être agréable à lire.
Cela ne veut pas dire qu'ils ne peuvent pas se transformer en spaghetti comme un cauchemar. Mais alors, qu'est-ce qui ne peut pas?
Bonne chasse au canard.
la source