Je lisais cette page , sur le moment où les getters/setters sont justifiés, et l'OP a donné l'exemple de code suivant:
class Fridge
{
int cheese;
void set_cheese(int _cheese) { cheese = _cheese; }
int get_cheese() { return cheese; }
}
void go_shopping(Fridge fridge)
{
fridge.set_cheese(fridge.get_cheese() + 5);
}
réponse acceptée indique:
Au fait, dans votre exemple, je donnerais à la classe
Fridge
les méthodesputCheese()
ettakeCheese()
, au lieu deget_cheese()
etset_cheese()
. Ensuite, vous auriez toujours l'encapsulation.
Comment l'encapsulation est-elle préservée en la renommant de get/set en putCheese()
/takeCheese()
Vous obtenez/définissez évidemment une valeur, alors pourquoi ne pas simplement la laisser comme get/set?
Dans la même réponse, il indique également:
Avoir des getters et des setters ne rompt pas en soi l'encapsulation. Ce qui rompt l'encapsulation, c'est l'ajout automatique d'un getter et d'un setter pour chaque membre de données (chaque champ, dans le jargon Java), sans y penser.
Dans ce cas, nous n'avons qu'une seule variable, cheese
, et vous voudrez peut-être prendre et retourner le fromage au réfrigérateur, donc une paire get/set dans ce cas est justifiée.
Je pense que tu ne comprends pas. Cela ne veut pas dire que vous devez renommer le setter et le getter, mais avoir des méthodes qui ajoutent et retirent des éléments du réfrigérateur. c'est à dire
public class Fridge
{
private int numberOfCheeseSlices;
public void AddCheeseSlices(int n)
{
if(n < 0) {
throw new Exception("you cant add negative cheese!");
}
if(numberOfCheeseSlices + n > this.capacityOfFridge) {
throw new Exception("TOO MUCH CHEESE!!");
}
..etc
this.numberOfCheeseSlices += n;
}
}
Maintenant, la variable privée est encapsulée. Je ne peux pas simplement le régler sur tout ce que je veux, je peux seulement ajouter et retirer des tranches de fromage du réfrigérateur en utilisant les méthodes, qui à leur tour garantissent que les règles de logique commerciale du fromage que je veux sont appliquées.
Les getters et setters cassent l'encapsulation à chaque fois , par définition. Ce que pourrait soutenir, c'est que parfois nous devons le faire. Avec cela à l'écart, voici mes réponses:
Comment l'encapsulation est-elle préservée en la renommant de get/set en putCheese ()/takeCheese () Vous obtenez évidemment/définissez une valeur, alors pourquoi ne pas simplement la laisser comme get/set?
La différence réside dans la sémantique, c'est-à-dire la signification de ce que vous écrivez. L'encapsulation ne consiste pas seulement à protéger, mais à masquer l'état interne. L'état interne ne devrait même pas être connu à l'extérieur, mais il est prévu que l'objet propose des méthodes pertinentes pour l'entreprise (parfois appelées "comportement") pour manipuler/utiliser cet état.
Donc get
et set
sont des termes techniques et semblent n'avoir rien à voir avec le domaine "frigo", tandis que put
et take
pourraient en fait avoir un sens .
Cependant , que put
ou take
rende réelle le sens dépend toujours des exigences et ne peut pas être jugé objectivement. Voir le point suivant:
Dans ce cas, nous n'avons qu'une seule variable, le fromage, et vous voudrez peut-être prendre et retourner le fromage au réfrigérateur, donc une paire get/set dans ce cas est justifiée.
Cela ne peut être objectivement déterminé sans plus de contexte. Votre exemple contient une méthode go_shopping()
ailleurs. Si c'est à cela que sert le Fridge
, alors pas besoin de get
ou set
, ce dont il a besoin est Fridge.go_shopping()
. De cette façon, vous avez une méthode qui est dérivée des exigences, toutes les "données" dont il a besoin sont locales et vous avez un comportement réel au lieu d'un simple structure de données voilée.
N'oubliez pas que vous ne construisez pas un Fridge
générique et réutilisable. Vous créez un Fridge
pour vos besoins uniquement. Tout effort dépensé pour en faire plus que ce qu'il doit être est en fait un gaspillage.
Presque tout cela montre une incompréhension fondamentale de l'encapsulation et de la manière dont elle s'applique.
La réponse initiale selon laquelle vous rompiez l'encapsulation est tout simplement fausse. Votre application peut avoir besoin de simplement définir la valeur du fromage dans le réfrigérateur au lieu d'augmenter/diminuer ou d'ajouter/supprimer. De plus, ce n'est pas de la sémantique, peu importe comment vous l'appelez, si vous avez besoin d'accéder et/ou de modifier des attributs, vous ne rompez pas l'encapsulation en les fournissant. Enfin, l'encapsulation ne consiste pas vraiment à "cacher", il s'agit de contrôler l'accès à l'état et aux valeurs qui ne devraient pas avoir besoin d'être publiques ou manipulées en dehors de la classe, tout en l'accordant à ceux qui le devraient et en effectuant la tâche mise à disposition en interne.
Un getter ou un setter ne rompt pas l'encapsulation lorsqu'il existe un besoin légitime d'obtenir ou de définir une valeur. C'est pourquoi les méthodes peuvent être rendues publiques.
L'encapsulation consiste à conserver les données et les méthodes qui modifient ces données directement ensemble dans un seul endroit logique, la classe.
Dans ce cas particulier, il est clairement nécessaire de modifier la valeur du fromage dans l'application. Quelle que soit la façon dont cela est fait, via get/set ou add/remove, tant que les méthodes sont encapsulées dans la classe, vous suivez le style orienté objet.
Pour plus de précision, je vais donner un exemple de la façon dont l'encapsulation est rompue en fournissant un accès indépendamment du nom de la méthode ou de l'exécution logique.
Supposons que votre réfrigérateur ait une "durée de vie", juste un certain nombre de tiques avant que le réfrigérateur ne soit plus opérationnel (pour des raisons d'argument, le réfrigérateur ne peut pas être réparé). Logiquement, il est impossible qu'un utilisateur (ou le reste de votre application) puisse modifier cette valeur. Cela devrait être privé. Il ne serait visible qu'à travers, par exemple, un attribut public différent appelé "isWorking". À l'expiration de la durée de vie, le réfrigérateur fonctionne en interne et fonctionne à faux.
L'exécution du décompte à vie et son actionnement de l'interrupteur isWorking sont toutes internes au réfrigérateur, rien à l'extérieur ne devrait/ne devrait pouvoir effectuer le processus. isWorking ne doit être visible que par conséquent, un getter ne rompt pas l'encapsulation. Cependant, l'ajout d'accesseurs pour des éléments du processus de durée de vie briserait votre encapsulation.
Comme la plupart des choses, la définition de l'encapsulation n'est pas littérale, elle est relative. Devriez-vous voir X en dehors de la classe? Devriez-vous pouvoir changer Y? Est-ce que tout ce qui s'applique à votre objet ici dans cette classe ou la fonctionnalité est-elle répartie sur plusieurs classes?
Il ne s'agit pas seulement de renommer une méthode. Les deux méthodes fonctionnent différemment.
(Imaginez ceci dans votre esprit)
get_cheese et set_cheese expose le fromage. putCheese () et takeCheese () gardent le fromage caché et prennent soin de le gérer et donnent à l'utilisateur un moyen de le gérer. L'observateur ne voit pas le fromage, il ne voit que deux méthodes pour le manipuler.