Je fais un grand projet pour la première fois. J'ai beaucoup de classes et certaines d'entre elles ont des variables publiques, certaines ont des variables privées avec les méthodes setter et getter et même les deux types.
J'ai décidé de réécrire ce code pour utiliser principalement un seul type. Mais je ne sais pas quoi utiliser (les variables qui ne sont utilisées que pour des méthodes dans le même objet sont toujours privées et ne font pas l'objet de cette question).
Je connais la théorie ce que signifie public et privé, mais qu'est-ce qui est utilisé dans le monde réel et pourquoi?
private
les membres de données sont généralement considérés comme bons car ils fournissent l'encapsulation.
Leur fournir des getters et des setters rompt cette encapsulation, mais c'est toujours mieux que les membres de données public
car il n'y a qu'un seul point d'accès à ces données.
Vous le remarquerez lors du débogage. Si c'est privé, vous savoir vous ne pouvez modifier la variable qu'à l'intérieur de la classe. S'il est public, vous devrez rechercher dans toute la base de code où ( il pourrait être modifié.
Autant que possible, bannissez les getters/setters et créez les propriétés private
. Cela suit le principe de la dissimulation d'informations - vous ne devez pas vous soucier des propriétés d'une classe. Il devrait être autonome. Bien sûr, dans la pratique, cela n'est pas possible, et si c'est le cas, une conception qui suit sera plus encombrée et plus difficile à entretenir qu'une autre.
C'est bien sûr une règle de base - par exemple, j'utiliserais simplement un struct
(équivalent à un class
avec accès public) pour, disons, une classe de points simple:
struct Point2D
{
double x;
double y;
};
Puisque vous dites que vous connaissez la théorie et que d'autres réponses ont creusé dans le sens de public/private, getters and setters, je voudrais me concentrer sur le pourquoi d'utiliser des accesseurs au lieu de créer des attributs publics (données de membre en C++) .
Imaginez que vous ayez un camion de classe dans un projet logistique:
class Truck {
public:
double capacity;
// lots of more things...
};
Si vous êtes nord-américain, vous utiliserez probablement des gallons pour représenter la capacité de vos camions. Imaginez que votre projet soit terminé, il fonctionne parfaitement, bien que de nombreuses utilisations directes de Truck::capacity
sont fait. En fait, votre projet devient un succès, donc une firme européenne vous demande de lui adapter votre projet; malheureusement, le projet devrait utiliser le système métrique maintenant, donc des litres au lieu de gallons devraient être utilisés pour la capacité.
Maintenant, cela pourrait être un gâchis. Bien sûr, une possibilité serait de préparer une base de code uniquement pour l'Amérique du Nord et une base de code uniquement pour l'Europe. Mais cela signifie que les corrections de bogues doivent être appliquées dans deux sources de code différentes, et cela est jugé impossible.
La solution est de créer une possibilité de configuration dans votre projet. L'utilisateur doit pouvoir régler des gallons ou des litres, au lieu que ce soit un choix fixe et câblé de gallons.
Avec l'approche vue ci-dessus, cela signifiera beaucoup de travail, vous devrez traquer toutes les utilisations de Truck::capacity
, et décidez quoi en faire. Cela signifiera probablement de modifier les fichiers le long de la base de code entière. Supposons, comme alternative, que vous ayez décidé d'une approche plus theoretic
.
class Truck {
public:
double getCapacity() const
{ return capacity; }
// lots of more things...
private:
double capacity;
};
Un éventuel changement alternatif n'implique aucune modification de l'interface de la classe:
class Truck {
public:
double getCapacity() const
{ if ( Configuration::Measure == Gallons ) {
return capacity;
} else {
return ( capacity * 3.78 );
}
}
// lots of more things...
private:
double capacity;
};
(Veuillez tenir compte du fait qu'il existe de nombreuses façons de le faire, que l'une n'est qu'une possibilité, et ce n'est qu'un exemple)
Vous devrez créer la configuration globale de la classe d'utilitaires (mais vous deviez quand même le faire) et ajouter une inclusion dans truck.h
pour configuration.h
, mais ce sont tous des changements locaux, le reste de votre base de code reste inchangé, évitant ainsi des bugs potentiels.
Enfin, vous déclarez également que vous travaillez actuellement dans un grand projet, qui, je pense, est le genre de domaine dans lequel ces raisons ont plus de sens. N'oubliez pas que l'objectif à garder à l'esprit lorsque vous travaillez dans de grands projets est de créer du code maintenable, c'est-à-dire du code que vous pouvez corriger et étendre avec de nouvelles fonctionnalités. Vous pouvez oublier les getters et setters dans les petits projets personnels, bien que j'essaye de m'y habituer.
J'espère que cela t'aides.
Il n'y a pas de règle stricte quant à ce qui doit être privé/public ou protégé.
Cela dépend du rôle de votre classe et de ce qu'elle offre.
D'un point de vue OOP getters/setters aident à l'encapsulation et doivent donc toujours être utilisés. Lorsque vous appelez un getter/setter, la classe peut faire tout ce qu'elle veut dans les coulisses et les internes du les cours ne sont pas exposés à l'extérieur.
D'un autre côté, d'un point de vue C++, cela peut également être un inconvénient si la classe fait beaucoup de choses inattendues lorsque vous voulez simplement obtenir/définir une valeur. Les gens aiment savoir si certains accès entraînent d'énormes frais généraux ou sont simples et efficaces. Lorsque vous accédez à une variable publique, vous savez exactement ce que vous obtenez, lorsque vous utilisez un getter/setter, vous n'en avez aucune idée.
Surtout si vous ne faites qu'un petit projet, passer votre temps à écrire des getters/setters et à les ajuster tous en conséquence lorsque vous décidez de changer le nom/type de votre variable/... produit beaucoup de travail occupé pour peu de gain. Vous feriez mieux de passer ce temps à écrire du code qui fait quelque chose d'utile.
Le code C++ n'utilise généralement pas de getters/setters lorsqu'ils ne fournissent pas de gain réel. Si vous concevez un projet de 1 000 000 lignes avec de nombreux modules qui doivent être aussi indépendants que possible, cela peut avoir du sens, mais pour la plupart des codes de taille normale que vous écrivez au jour le jour, ils sont exagérés.
Il existe certains types de données dont le seul but est de conserver des données bien spécifiées. Ceux-ci peuvent généralement être écrits sous forme de structures avec des membres de données publics. Hormis cela, une classe devrait définir une abstraction. Les variables publiques ou les setters et getters triviaux suggèrent que le design n'a pas été suffisamment réfléchi, résultant en une agglomération d'abstractions faibles qui n'abstiennent pas grand-chose. Au lieu de penser aux données, pensez à comportement: cette classe devrait faire X, Y et Z. À partir de là, décidez quelles données internes sont nécessaires pour prendre en charge le comportement souhaité. Ce n'est pas facile au début, mais rappelez-vous que c'est le comportement qui compte, pas les données.
Les variables de membre privé sont préférées aux variables de membre public, principalement pour les raisons indiquées ci-dessus (encapsulation, données bien spécifiées, etc.). Ils fournissent également une certaine protection des données, car ils garantissent qu'aucune entité extérieure ne peut modifier la variable membre sans passer par le canal approprié d'un setter si nécessaire.
Un autre avantage des getters et setters est que si vous utilisez un IDE (comme Eclipse ou Netbeans), vous pouvez utiliser la fonctionnalité de l'IDE pour rechercher chaque place dans le base de code où la fonction est appelée. Ils fournissent une visibilité sur l'endroit où un élément de données dans cette classe particulière est utilisé ou modifié. De plus, vous pouvez facilement sécuriser l'accès au thread des variables membres en ayant un mutex interne. Le getter/les fonctions de définition saisiraient ce mutex avant d'accéder ou de modifier la variable.
Je suis partisan de l'abstraction au point où elle est toujours utile. L'abstraction pour l'abstraction se traduit généralement par un désordre encombré qui est plus compliqué que sa valeur.
Les variables publiques sont généralement déconseillées, et la meilleure forme est de rendre toutes les variables privées et d'y accéder avec des getters et des setters:
private int var;
public int getVar() {
return var;
}
public void setVar(int _var) {
var = _var;
}
Les IDE modernes comme Eclipse et d'autres vous aident à le faire en fournissant des fonctionnalités telles que "Implémenter les Getters et Setters" et "Encapsulate Field" (qui remplace tous les accès directs aux variables par les appels getter et setter correspondants).