Depende do significado real de a, be getProduct.
O objetivo dos getters é poder alterar a implementação real, mantendo a interface do objeto igual. Por exemplo, se um dia getAse 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 getProductcontinuar 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 getProductusar ae bdiretamente, 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 getIdpoderá 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á umagetIdchamada 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.