Depende do significado real de a
, b
e getProduct
.
O objetivo dos getters é poder alterar a implementação real, mantendo a interface do objeto igual. Por exemplo, se um dia getA
se tornar return a + 1;
, a alteração será localizada em um getter.
Casos de cenários reais às vezes são mais complicados do que um campo de apoio constante atribuído por meio de um construtor associado a um getter. Por exemplo, o valor do campo pode ser calculado ou carregado em um banco de dados na versão original do código. Na próxima versão, o armazenamento em cache pode ser adicionado para otimizar o desempenho. Se getProduct
continuar usando a versão computada, ela não se beneficiará do cache (ou o mantenedor fará a mesma alteração duas vezes).
Se faz sentido getProduct
usar a
e b
diretamente, use-os. Caso contrário, use getters para evitar problemas de manutenção posteriormente.
Exemplo onde alguém usaria 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;
}
Embora, no momento, o getter não contenha nenhuma lógica de negócios, não é de excluir que a lógica no construtor será migrada para o getter, a fim de evitar o trabalho do banco de dados ao inicializar o 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;
}
Posteriormente, o cache pode ser adicionado (em C #, seria usado Lazy<T>
, tornando o código curto e fácil; não sei se existe um equivalente em 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;
}
Ambas as alterações foram focadas no getter e no campo de suporte, o código restante não sendo afetado. Se, em vez disso, eu tivesse usado um campo em vez de um getter getPriceWithRebate
, também teria que refletir as alterações lá.
Exemplo em que alguém provavelmente usaria 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;
}
O getter é direto: é uma representação direta de um campo constante (semelhante ao de C # readonly
) que não deve mudar no futuro: é provável que o getter de ID nunca se torne um valor calculado. Portanto, mantenha-o simples e acesse o campo diretamente.
Outro benefício é que ele getId
poderá ser removido no futuro se parecer que não é usado fora (como no trecho de código anterior).
const
: Suponho que isso significa que o compilador fará umagetId
chamada em linha de qualquer maneira e permite que você faça alterações em qualquer direção. (Caso contrário, concordo plenamente com seus motivos para usar getters.) E nos idiomas que fornecem sintaxe de propriedade, há ainda menos motivos para não usar a propriedade em vez do campo de apoio diretamente.