Рискуя спорить, я вернусь к противоположной точке зрения, с которой я впервые столкнулся при чтении "Holub on Patterns". Это была очень сложная точка зрения, но после размышления она имела смысл:
Геттеры и сеттеры злые
Использование геттеров и сеттеров противоречит основам объектно-ориентированного проектирования: абстракции данных и инкапсуляции. Чрезмерное использование методов получения и установки сделает ваш код менее гибким и поддерживаемым в долгосрочной перспективе. В конечном итоге они предоставляют базовую реализацию вашего класса, фиксируя детали реализации в интерфейсе класса.
Представьте, что ваше поле 'std :: string Foo :: bar' должно измениться с std :: string на другой класс строки, который, скажем, лучше оптимизирован или поддерживает другой набор символов. Вам нужно изменить поле личных данных, метод получения, метод установки и весь клиентский код этого класса, который вызывает эти методы получения и установки.
Вместо того, чтобы проектировать свои классы для «предоставления данных» и «получения данных», проектируйте их для «выполнения операций» или «предоставления услуг». Спросите себя, почему вы пишете функцию «GetBar». Что вы делаете с этими данными? Возможно, вы отображаете эти данные или выполняете их обработку. Этот процесс лучше раскрыть как метод Foo?
Это не значит, что геттеры и сеттеры не имеют своей цели. В C # я считаю, что фундаментальная причина их использования - это взаимодействие с IDE-дизайном Visual Studio GUI, но если вы пишете их на C ++, вероятно, лучше сделать шаг назад, посмотреть на свой дизайн и посмотреть, есть ли что-то. отсутствует.
Я попытаюсь смоделировать пример, чтобы проиллюстрировать.
// A class that represents a user's bank account
class Account {
private:
int balance_; // in cents, lets say
public:
const int& GetBalance() { return balance_; }
void SetBalance(int b) { balance_ = b; }
};
class Deposit {
private:
int ammount_;
public:
const int& GetAmount() { return ammount_; }
void SetAmmount(int a) { _balance = a; }
};
void DoStuffWithAccount () {
Account a;
// print account balance
int balance = a.GetBalance();
std::cout << balance;
// deposit some money into account
Deposit d(10000);
a.SetBalance( a.GetBalance() + d.GetValue());
}
Это не займет много времени, чтобы увидеть, что это очень плохо разработано.
- Целые числа - ужасный тип данных валюты
- Депозит должен быть функцией Счета
Методы получения и установки затрудняют решение проблем, поскольку клиентский код DoStuffWithAccount теперь привязан к типу данных, который мы использовали для реализации баланса счета.
Итак, давайте пропустим этот код и посмотрим, что мы можем улучшить
// A class that represents a user's bank account
class Account {
private:
float balance_;
public:
void Deposit(float b) { balance_ += b; }
void Withdraw(float w) { balance_ -= w; }
void DisplayDeposit(std::ostream &o) { o << balance_; }
};
void DoStuffWithAccount () {
Account a;
// print account balance
a.DisplayBalance(std::cout);
// deposit some money into account
float depositAmt = 1000.00;
a.Deposit(depositAmt);
a.DisplayBalance(std::cout);
}
«Поплавок» - это шаг в правильном направлении. Конечно, вы могли бы изменить внутренний тип на «float» и при этом поддерживать идиому getter / setter:
class Account {
private:
// int balance_; // old implementation
float balance_;
public:
// support the old interface
const int& GetBalance() { return (int) balance_; }
void SetBalance(int b) { balance_ = b; }
// provide a new interface for the float type
const float& GetBalance() { return balance_; } // not legal! how to expose getter for float as well as int??
void SetBalance(float b) { balance_ = b; }
};
, но это не займет много времени, чтобы понять, что механизм получения / установки удваивает вашу рабочую нагрузку и усложняет ситуацию, так как вам нужно поддерживать как код, который использовал целые, так и новый код, который будет использовать плавающие числа. Функция депозита позволяет немного расширить диапазон типов депозита.
Класс, подобный Учетной записи, вероятно, не лучший пример, так как «получение» баланса учетной записи является естественной операцией для Учетной записи. Однако в целом вы должны быть осторожны с геттерами и сеттерами. Не приучайте писать геттеры и сеттеры для каждого члена данных. Это довольно легко разоблачить и заблокировать себя в реализации, если вы не будете осторожны.