Depende del significado real de a
, b
y getProduct
.
El propósito de los captadores es poder cambiar la implementación real manteniendo la interfaz del objeto igual. Por ejemplo, si un día se getA
convierte return a + 1;
, el cambio se localiza en un captador.
Los casos de escenarios reales a veces son más complicados que un campo de respaldo constante asignado a través de un constructor asociado con un getter. Por ejemplo, el valor del campo puede calcularse o cargarse desde una base de datos en la versión original del código. En la próxima versión, se puede agregar el almacenamiento en caché para optimizar el rendimiento. Si getProduct
continúa utilizando la versión calculada, no se beneficiará del almacenamiento en caché (o el mantenedor hará el mismo cambio dos veces).
Si tiene sentido getProduct
usarlo a
y usarlo b
directamente, úsalos. De lo contrario, use getters para evitar problemas de mantenimiento más adelante.
Ejemplo donde uno usaría 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;
}
Si bien por el momento, el captador no contiene ninguna lógica de negocios, no se excluye que la lógica en el constructor se migre al captador para evitar hacer el trabajo de la base de datos al inicializar el objeto:
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;
}
Más tarde, se puede agregar el almacenamiento en caché (en C #, uno usaría Lazy<T>
, haciendo que el código sea corto y fácil; no sé si hay un equivalente 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;
}
Ambos cambios se centraron en el captador y el campo de respaldo, y el código restante no se vio afectado. Si, en cambio, hubiera usado un campo en lugar de un captador getPriceWithRebate
, también tendría que reflejar los cambios allí.
Ejemplo donde probablemente se usarían campos privados:
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;
}
El captador es sencillo: es una representación directa de un campo constante (similar al de C # readonly
) que no se espera que cambie en el futuro: lo más probable es que el captador de ID nunca se convierta en un valor calculado. Así que manténgalo simple y acceda al campo directamente.
Otro beneficio es que getId
podría eliminarse en el futuro si parece que no se usa en el exterior (como en el fragmento de código anterior).
const
: Supongo que eso significa que el compilador en líneagetId
llamará de todos modos y le permite hacer cambios en cualquier dirección. (De lo contrario, estoy totalmente de acuerdo con sus razones para usar getters). Y en los lenguajes que proporcionan sintaxis de propiedad, hay incluso menos razones para no usar la propiedad en lugar del campo de respaldo directamente.