Es hängt von der tatsächlichen Bedeutung a
, b
und getProduct
.
Der Zweck von Getter besteht darin, die tatsächliche Implementierung ändern zu können, während die Schnittstelle des Objekts gleich bleibt. Wenn beispielsweise eines Tages wird, getA
wird return a + 1;
die Änderung in einem Getter lokalisiert.
Reale Szenariofälle sind manchmal komplizierter als ein konstantes Hintergrundfeld, das über einen Konstruktor zugewiesen wird, der einem Getter zugeordnet ist. Beispielsweise kann der Wert des Feldes in der Originalversion des Codes berechnet oder aus einer Datenbank geladen werden. In der nächsten Version kann Caching hinzugefügt werden, um die Leistung zu optimieren. Wenn getProduct
die berechnete Version weiterhin verwendet wird, profitiert sie nicht vom Caching (oder der Betreuer nimmt dieselbe Änderung zweimal vor).
Wenn es für getProduct
die Verwendung a
und b
direkt sinnvoll ist, verwenden Sie sie. Verwenden Sie andernfalls Getter, um spätere Wartungsprobleme zu vermeiden.
Beispiel, wo man Getter verwenden würde:
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;
}
Während der Getter derzeit keine Geschäftslogik enthält, wird nicht ausgeschlossen, dass die Logik im Konstruktor auf den Getter migriert wird, um Datenbankarbeiten beim Initialisieren des Objekts zu vermeiden:
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;
}
Später kann Caching hinzugefügt werden (in C # würde man verwenden Lazy<T>
, um den Code kurz und einfach zu machen; ich weiß nicht, ob es in C ++ ein Äquivalent gibt):
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;
}
Beide Änderungen konzentrierten sich auf den Getter und das Hintergrundfeld, wobei der verbleibende Code nicht betroffen war. Wenn ich stattdessen ein Feld anstelle eines Getters verwendet hätte getPriceWithRebate
, müsste ich auch die Änderungen dort widerspiegeln.
Beispiel, wo man wahrscheinlich private Felder verwenden würde:
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;
}
Der Getter ist unkompliziert: Es handelt sich um eine direkte Darstellung eines konstanten readonly
Feldes (ähnlich dem von C # ), von dem nicht erwartet wird, dass es sich in Zukunft ändert: Es besteht die Möglichkeit, dass der ID-Getter niemals zu einem berechneten Wert wird. Halten Sie es einfach und greifen Sie direkt auf das Feld zu.
Ein weiterer Vorteil ist, dass das getId
möglicherweise in Zukunft entfernt wird, wenn es den Anschein hat, dass es nicht im Freien verwendet wird (wie im vorherigen Code).
const
: Ich gehe davon aus, dass der Compiler einengetId
Aufruf trotzdem einbindet und Sie Änderungen in beide Richtungen vornehmen können. (Ansonsten stimme ich Ihren Gründen für die Verwendung von Gettern voll und ganz zu .) Und in Sprachen, die Eigenschaftssyntax bieten, gibt es noch weniger Gründe, die Eigenschaft nicht direkt als das Hintergrundfeld zu verwenden.