Это зависит от фактического значения a
, b
и getProduct
.
Цель геттеров - иметь возможность изменять фактическую реализацию, сохраняя интерфейс объекта неизменным. Например, если один день getA
становится return a + 1;
, изменение локализуется для получателя.
Реальные сценарии иногда бывают более сложными, чем постоянное вспомогательное поле, назначаемое через конструктор, связанный с геттером. Например, значение поля может быть вычислено или загружено из базы данных в исходной версии кода. В следующей версии может быть добавлено кэширование для оптимизации производительности. Если getProduct
продолжит использовать вычисленную версию, он не получит выгоду от кэширования (или сопровождающий сделает то же самое изменение дважды).
Если это имеет смысл для getProduct
использования a
и b
непосредственно, используйте их. В противном случае используйте геттеры для предотвращения проблем с обслуживанием позже.
Пример, где можно использовать геттеры:
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;
}
Хотя на данный момент получатель не содержит никакой бизнес-логики, не исключено, что логика в конструкторе будет перенесена на получатель, чтобы избежать работы с базой данных при инициализации объекта:
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;
}
Позже может быть добавлено кеширование (в C # можно было бы использовать его Lazy<T>
, делая код коротким и простым; я не знаю, есть ли эквивалент в 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;
}
Оба изменения были сосредоточены на получателе и поле поддержки, оставшийся код не был затронут. Если бы вместо этого я использовал поле вместо получателя getPriceWithRebate
, мне бы пришлось отразить и там изменения.
Пример, где можно использовать закрытые поля:
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;
}
Получатель прост: это прямое представление поля константы (аналогично C # readonly
), которое не ожидается в будущем: скорее всего, идентификатор геттера никогда не станет вычисленным значением. Так что будьте проще и получите доступ к полю напрямую.
Другое преимущество заключается в том, что getId
в будущем он может быть удален, если окажется, что он не используется снаружи (как в предыдущем фрагменте кода).
const
: я предполагаю, что это означает, что компилятор вgetId
любом случае встроит вызов и позволит вам вносить изменения в любом направлении. ( В противном случае я полностью согласен с вашими причинами для использования добытчиков.) И в языках , которые предоставляют синтаксис свойств, есть даже меньше причины не использовать свойство , а не поле подложки непосредственно.