Cela dépend de la signification réelle de a
, b
et getProduct
.
Le but des getters est de pouvoir changer l'implémentation réelle tout en gardant l'interface de l'objet la même. Par exemple, si un jour, getA
devient return a + 1;
, le changement est localisé sur un getter.
Les cas de scénarios réels sont parfois plus compliqués qu'un champ de support constant attribué par un constructeur associé à un getter. Par exemple, la valeur du champ peut être calculée ou chargée à partir d'une base de données dans la version d'origine du code. Dans la prochaine version, la mise en cache peut être ajoutée pour optimiser les performances. Si getProduct
continue d'utiliser la version calculée, il ne bénéficiera pas de la mise en cache (ou le responsable effectuera la même modification deux fois).
S'il est parfaitement logique getProduct
d'utiliser a
et b
directement, utilisez-les. Sinon, utilisez des getters pour éviter des problèmes de maintenance ultérieurement.
Exemple où l'on utiliserait des getters:
class Product {
public:
Product(ProductId id) : {
price = Money.fromCents(
data.findProductById(id).price,
environment.currentCurrency
)
}
Money getPrice() {
return price;
}
Money getPriceWithRebate() {
return getPrice().applyRebate(rebate); // ← Using a getter instead of a field.
}
private:
Money price;
}
Alors que pour le moment, le getter ne contient aucune logique métier, il n'est pas exclu que la logique du constructeur soit migrée vers le getter afin d'éviter de faire un travail de base de données lors de l'initialisation de l'objet:
class Product {
public:
Product(ProductId id) : id(id) { }
Money getPrice() {
return Money.fromCents(
data.findProductById(id).price,
environment.currentCurrency
)
}
Money getPriceWithRebate() {
return getPrice().applyRebate(rebate);
}
private:
const ProductId id;
}
Plus tard, la mise en cache peut être ajoutée (en C #, on utiliserait Lazy<T>
, ce qui rend le code court et facile; je ne sais pas s'il y a un équivalent en C ++):
class Product {
public:
Product(ProductId id) : id(id) { }
Money getPrice() {
if (priceCache == NULL) {
priceCache = Money.fromCents(
data.findProductById(id).price,
environment.currentCurrency
)
return priceCache;
}
Money getPriceWithRebate() {
return getPrice().applyRebate(rebate);
}
private:
const ProductId id;
Money priceCache;
}
Les deux modifications étaient axées sur le getter et le champ de support, le code restant n'étant pas affecté. Si, à la place, j'avais utilisé un champ au lieu d'un getter getPriceWithRebate
, je devrais également y refléter les changements.
Exemple où l'on utiliserait probablement des champs privés:
class Product {
public:
Product(ProductId id) : id(id) { }
ProductId getId() const { return id; }
Money getPrice() {
return Money.fromCents(
data.findProductById(id).price, // ← Accessing `id` directly.
environment.currentCurrency
)
}
private:
const ProductId id;
}
Le getter est simple: il s'agit d'une représentation directe d'un readonly
champ constant (similaire à C # ) qui ne devrait pas changer à l'avenir: il y a de fortes chances que l'ID getter ne devienne jamais une valeur calculée. Alors restez simple et accédez directement au terrain.
Un autre avantage est que le getId
fichier pourrait être supprimé à l'avenir s'il apparaît qu'il n'est pas utilisé à l'extérieur (comme dans le code précédent).
const
: je suppose que cela signifie que le compilateur insérera ungetId
appel de toute façon et cela vous permet de faire des changements dans les deux sens. (Sinon, je suis entièrement d'accord avec vos raisons d' utiliser des getters.) Et dans les langues qui fournissent la syntaxe des propriétés, il y a encore moins de raisons de ne pas utiliser la propriété plutôt que le champ de support directement.En règle générale, vous utiliseriez directement les variables. Vous vous attendez à changer tous les membres lorsque vous modifiez l'implémentation d'une classe. Ne pas utiliser directement les variables rend simplement plus difficile l'isolement correct du code qui en dépend et rend la lecture du membre plus difficile.
Ceci est bien sûr différent si les getters implémentent une vraie logique, dans ce cas cela dépend si vous devez utiliser leur logique ou non.
la source
Je dirais que l'utilisation des méthodes publiques serait préférable, sinon pour toute autre raison, mais pour se conformer à DRY .
Je sais que dans votre cas, vous avez des champs de support simples pour vos accesseurs, mais vous pouvez avoir une certaine logique, par exemple du code de chargement paresseux, que vous devez exécuter avant la première fois que vous utilisez cette variable. Ainsi, vous voudriez appeler vos accesseurs au lieu de référencer directement vos champs. Même si vous n'avez pas cela dans ce cas, il est logique de s'en tenir à une seule convention. De cette façon, si jamais vous changez votre logique, vous n'avez qu'à la changer en un seul endroit.
la source
Pour une classe, cette petite simplicité gagne. Je voudrais simplement utiliser un * b.
Pour quelque chose de beaucoup plus compliqué, j'envisagerais fortement d'utiliser getA () * getB () si je voulais séparer clairement l'interface "minimale" de toutes les autres fonctions de l'API publique complète. Un excellent exemple serait std :: string en C ++. Il a 103 fonctions membres, mais seulement 32 d'entre eux ont vraiment besoin d' accéder à des membres privés. Si vous aviez une classe aussi complexe, forcer toutes les fonctions "non essentielles" à passer systématiquement par "l'API principale" pourrait rendre l'implémentation beaucoup plus facile à tester, déboguer et refactoriser.
la source
getA() * getB()
c'est mieux à moyen et long terme.