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.