Je me souviens d'avoir eu un argument similaire avec mon conférencier lors de l'apprentissage de C ++ à l'université. Je ne pouvais tout simplement pas comprendre l'intérêt d'utiliser des accesseurs et des setters quand je pouvais rendre un public variable. Je comprends mieux maintenant avec des années d’expérience et j’ai appris une meilleure raison que de simplement dire "pour maintenir l’encapsulation".
En définissant les getters et les setters, vous fournissez une interface cohérente de sorte que, si vous souhaitez modifier votre implémentation, vous aurez moins de chances de casser du code dépendant. Ceci est particulièrement important lorsque vos classes sont exposées via une API et utilisées dans d'autres applications ou par des tiers. Alors, qu'en est-il des choses qui vont dans le getter ou le setter?
Les accesseurs sont généralement mieux mis en œuvre en tant que simple passe-partout simplifié pour accéder à une valeur, car cela rend leur comportement prévisible. Je dis en général, car j’ai vu des cas où des accesseurs ont été utilisés pour accéder à des valeurs manipulées par calcul ou même par code conditionnel. Généralement pas si bon si vous créez des composants visuels pour une utilisation au moment de la conception, mais apparemment pratique au moment de l'exécution. Cependant, il n'y a pas de réelle différence entre cela et l'utilisation d'une méthode simple, sauf que lorsque vous utilisez une méthode, vous êtes généralement plus susceptible de nommer une méthode de manière plus appropriée, de sorte que la fonctionnalité du "getter" soit plus apparente lors de la lecture du code.
Comparez ce qui suit:
int aValue = MyClass.Value;
et
int aValue = MyClass.CalculateValue();
La deuxième option indique clairement que la valeur est en cours de calcul, alors que le premier exemple vous indique que vous renvoyez simplement une valeur sans rien connaître de la valeur elle-même.
Vous pourriez peut-être soutenir que ce qui suit serait plus clair:
int aValue = MyClass.CalculatedValue;
Le problème, cependant, est que vous supposez que la valeur a déjà été manipulée ailleurs. Ainsi, dans le cas d'un getter, bien que vous souhaitiez supposer que quelque chose d'autre se passe lorsque vous renvoyez une valeur, il est difficile de préciser ces choses dans le contexte d'une propriété et les noms de propriété ne doivent jamais contenir de verbes. sinon, il est difficile de comprendre d'un coup d'œil si le nom utilisé doit être décoré de parenthèses lors de l'accès.
Les Setters sont un cas légèrement différent cependant. Il est tout à fait approprié qu'un ouvreur fournisse un traitement supplémentaire afin de valider les données soumises à une propriété, en levant une exception si définir une valeur enfreindrait les limites définies de la propriété. Le problème rencontré par certains développeurs lors de l’ajout de traitement aux paramètres est toutefois qu’il est toujours tentant de laisser un peu plus de travail, par exemple effectuer un calcul ou une manipulation des données. C’est là que vous pouvez avoir des effets secondaires qui peuvent parfois être imprévisibles ou indésirables.
Dans le cas des setters, j'applique toujours une règle simple, qui consiste à utiliser le moins possible les données. Par exemple, j'autorise généralement les tests des limites et les arrondis afin de pouvoir lever des exceptions si nécessaire ou d'éviter des exceptions inutiles lorsque celles-ci peuvent être évitées de manière raisonnable. Les propriétés de virgule flottante sont un bon exemple où vous pouvez arrondir le nombre de décimales excessif pour éviter de générer une exception, tout en permettant de saisir les valeurs de plage avec quelques décimales supplémentaires.
Si vous appliquez une sorte de manipulation de l'entrée du setter, vous rencontrez le même problème que pour le getter: il est difficile de permettre aux autres de savoir ce que fait le setter en le nommant simplement. Par exemple:
MyClass.Value = 12345;
Est-ce que cela vous dit quelque chose sur ce qui va arriver à la valeur quand elle est donnée au passeur?
Que diriez-vous:
MyClass.RoundValueToNearestThousand(12345);
Le deuxième exemple vous indique exactement ce qui va arriver à vos données, tandis que le premier ne vous laissera pas savoir si votre valeur va être modifiée de manière arbitraire. Lors de la lecture du code, le deuxième exemple sera beaucoup plus clair dans son but et sa fonction.
Ai-je raison de penser que cela irait complètement à l'encontre du but recherché, à savoir que les getters et les setters soient autorisés en premier lieu, et que la validation et toute autre logique (sans effets secondaires étranges bien sûr) soient autorisées?
Avoir des getters et des setters ne concerne pas l’encapsulation pour des raisons de «pureté», mais l’encapsulation afin de permettre au code d’être facilement refactorisé sans risquer de modifier l’interface de la classe qui risquerait sinon de briser la compatibilité de la classe avec le code appelant. La validation est tout à fait appropriée dans un setter, mais il existe un faible risque qu'une modification de la validation puisse rompre la compatibilité avec le code appelant si le code appelant repose sur la validation effectuée de manière particulière. C’est une situation généralement rare et présentant un risque relativement faible, mais il convient de le noter pour des raisons de complétude.
Quand la validation doit-elle avoir lieu?
La validation doit avoir lieu dans le contexte du paramètre avant de définir réellement la valeur. Cela garantit que si une exception est levée, l'état de votre objet ne changera pas et invalidera potentiellement ses données. Je trouve généralement préférable de déléguer la validation à une méthode distincte, qui serait la première chose appelée dans le programme de définition, afin de garder le code de définition relativement peu encombré.
Un installateur est-il autorisé à changer la valeur (peut-être convertir une valeur valide en une représentation interne canonique)?
Dans de très rares cas, peut-être. En général, il vaut probablement mieux ne pas le faire. C'est le genre de chose qu'il vaut mieux laisser à une autre méthode.