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.Normalerweise verwenden Sie die Variablen direkt. Sie erwarten, dass Sie alle Mitglieder ändern, wenn Sie die Implementierung einer Klasse ändern. Wenn Sie die Variablen nicht direkt verwenden, wird es einfacher, den von ihnen abhängigen Code korrekt zu isolieren, und das Lesen des Elements wird schwieriger.
Dies ist natürlich anders, wenn die Getter echte Logik implementieren. In diesem Fall hängt es davon ab, ob Sie ihre Logik verwenden müssen oder nicht.
quelle
Ich würde sagen, dass die Verwendung der öffentlichen Methoden vorzuziehen wäre, wenn nicht aus einem anderen Grund, als um DRY zu entsprechen .
Ich weiß, dass Sie in Ihrem Fall einfache Sicherungsfelder für Ihre Accessoren haben, aber Sie könnten eine bestimmte Logik haben, z. B. einen Code zum verzögerten Laden, den Sie ausführen müssen, bevor Sie diese Variable zum ersten Mal verwenden. Daher möchten Sie Ihre Accessoren anrufen, anstatt direkt auf Ihre Felder zu verweisen. Auch wenn Sie dies in diesem Fall nicht haben, ist es sinnvoll, sich an eine einzige Konvention zu halten. Auf diese Weise müssen Sie Ihre Logik nur an einer Stelle ändern, wenn Sie sie jemals ändern.
quelle
Für eine Klasse gewinnt diese kleine Einfachheit. Ich würde nur a * b verwenden.
Für etwas weitaus Komplizierteres würde ich die Verwendung von getA () * getB () in Betracht ziehen, wenn ich die "minimale" Schnittstelle klar von allen anderen Funktionen in der vollständigen öffentlichen API trennen möchte. Ein hervorragendes Beispiel wäre std :: string in C ++. Es hat 103 Mitgliederfunktionen, aber nur 32 von ihnen benötigen wirklich Zugriff auf private Mitglieder. Wenn Sie eine so komplexe Klasse hätten, könnte das Erzwingen, dass alle "Nicht-Kern" -Funktionen konsistent die "Kern-API" durchlaufen, das Implementieren, Debuggen und Refaktorisieren der Implementierung erheblich vereinfachen.
quelle
getA() * getB()
ist es mittel- und langfristig besser.