Я переписал свою игру в блэкджек на C++, применив больше ООП-подхода. Одно я знаю наверняка: переменные класса не должны быть общедоступными по соображениям безопасности, и если переменная необходима автономная, вы должны создать для нее геттеры и сеттеры. Проблема в том, что мой код в main.cpp
слишком часто использует эти переменные. Кроме того, их так много, что установка каждого из них будет раздражать.
Например, это часть класса Account
и Hand
:
class Account {
private:
std::fstream saveFile;
public:
std::string name;
int tokenAmount, betAmount;
bool doubledDown, splitted;
bool canDoubleDown, canSplit;
int choseToStand;
void registerUser();
void saveBalance();
//[...](constructor)
class Hand {
public:
std::vector<std::pair<std::string, std::string>> cards;
std::pair<bool, bool> handBusted;
int cardSum;
void display();
int sum();
bool checkForBj();
};
Строка этого кода:
std::cout << "Decision: [1] Stand, [2] Hit";
if (!player.splitted && !player.doubledDown && roundNumber == 1 && player.betAmount <= player.tokenAmount) {
std::cout << ", [3] Double down";
player.canDoubleDown = true;
}
было бы настолько полно этих функций, что оно стало бы нечитаемым, или, может быть, я просто недостаточно образован. У меня вопрос, имеет ли смысл настраивать геттеры и сеттеры в моем случае, и если да, то требует ли я переписывать этот код или он должен выглядеть так? потому что я искренне не знаю.
Элементы данных практически никогда не связаны друг с другом, и установка одного из них чаще всего приводит к тому, что информация в каком-либо другом элементе становится неверной. Поэтому отдавайте предпочтение более крупным операциям. Например, из вашей логики canDoubleDown выглядит не как флаг, а как функция-член, которой требуется roundNumber в качестве входных данных для определения его значения.
вам следует отнести это на codereview.stackexchange.com, я могу отметить одну вещь: ваш класс учетной записи больше похож на конечный автомат, возможно, вам стоит прочитать о конечных автоматах.
Вы можете переместить эти функции/условия в класс. Таким образом, вместо длинного условия в main у вас будет что-то вроде этого:
bool Account::CanDoubleDown() {
return !splitted && !doubledDown && (betAmount <= tokenAmount);
}
Это, вероятно, также исключит переменные-члены canSplit и canDoubleDown - они должны быть функциями-членами, возвращающими bool.
Из приведенного вами контекста неясно, откуда берется roundNumber, поэтому это может быть либо параметр, либо единственное условие, остающееся в main():
if (roundNumber == 1 && player.CanDoubleDown()) {
std::cout << ", [3] Double down";
}
Недостаточно контекста, чтобы знать наверняка, но нет необходимости хранить canDoubleDown как логическое значение в Account — с функцией-членом все в порядке.
CanDoubleDown
понадобится int roundNumber
в качестве параметра (или bool isRoundOne
)
Возможно, но в исходном вопросе не так уж много контекста — это не «исполняемый» код. Однако я добавлю это к своему ответу.
int roundNumber
единственная функция — определить, может ли игрок удвоить ставку или нет, так что да, я мог бы заменить ее, но я добавил ее на случай появления новых функций. Я знаю, что такое ООП, но у меня не было возможности понять, где это реализовать, и вы это прекрасно объяснили.
@dheb Рад помочь! Если это достаточно ответило на ваш вопрос, вы тоже можете «принять» ответ.
Всегда нужны геттеры и сеттеры, вы тормозите инкапсуляцию. Даже если публичные члены выглядят лучше, у вас есть внешний доступ к вашему классу, который в идеальном мире не нужен. Если вам нужны геттеры/сеттеры, подумайте еще раз о своем дизайне!