Dipende dal significato effettivo di a
, b
e getProduct
.
Lo scopo dei Getters è di essere in grado di cambiare l'effettiva implementazione mantenendo allo stesso modo l'interfaccia dell'oggetto. Ad esempio, se un giorno, getA
diventa return a + 1;
, la modifica è localizzata in un getter.
I casi di scenario reale sono talvolta più complicati di un campo di supporto costante assegnato tramite un costruttore associato a un getter. Ad esempio, il valore del campo può essere calcolato o caricato da un database nella versione originale del codice. Nella prossima versione, è possibile aggiungere il caching per ottimizzare le prestazioni. Se getProduct
continua a utilizzare la versione calcolata, non trarrà vantaggio dalla memorizzazione nella cache (o il maintainer eseguirà la stessa modifica due volte).
Se ha perfettamente senso per getProduct
utilizzare a
e b
direttamente, usali. Altrimenti, usa i getter per evitare problemi di manutenzione più tardi.
Esempio di utilizzo dei getter:
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;
}
Mentre per il momento, il getter non contiene alcuna logica di business, non è escluso che la logica nel costruttore verrà migrata al getter per evitare di fare il lavoro del database durante l'inizializzazione dell'oggetto:
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;
}
In seguito, è possibile aggiungere il caching (in C #, si userebbe Lazy<T>
, rendendo il codice breve e facile, non so se esiste un equivalente in 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;
}
Entrambe le modifiche erano focalizzate sul getter e sul backing field, il restante codice non veniva modificato. Se, invece, avessi usato un campo invece di un getter in getPriceWithRebate
, dovrei riflettere anche le modifiche.
Esempio in cui probabilmente si useranno campi privati:
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;
}
Il getter è semplice: è una rappresentazione diretta di un campo costante (simile al readonly
di C #) che non dovrebbe cambiare in futuro: è probabile che ID getter non diventerà mai un valore calcolato. Quindi mantienilo semplice e accedi direttamente al campo.
Un altro vantaggio è che la getId
potrebbe essere rimossa in futuro se sembra che non sia utilizzata all'esterno (come nel codice precedente).