Безопасная инкапсуляция многих переменных C++

Я переписал свою игру в блэкджек на 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;
}

было бы настолько полно этих функций, что оно стало бы нечитаемым, или, может быть, я просто недостаточно образован. У меня вопрос, имеет ли смысл настраивать геттеры и сеттеры в моем случае, и если да, то требует ли я переписывать этот код или он должен выглядеть так? потому что я искренне не знаю.

Всегда нужны геттеры и сеттеры, вы тормозите инкапсуляцию. Даже если публичные члены выглядят лучше, у вас есть внешний доступ к вашему классу, который в идеальном мире не нужен. Если вам нужны геттеры/сеттеры, подумайте еще раз о своем дизайне!

Klaus 02.08.2024 10:28

Элементы данных практически никогда не связаны друг с другом, и установка одного из них чаще всего приводит к тому, что информация в каком-либо другом элементе становится неверной. Поэтому отдавайте предпочтение более крупным операциям. Например, из вашей логики canDoubleDown выглядит не как флаг, а как функция-член, которой требуется roundNumber в качестве входных данных для определения его значения.

Öö Tiib 02.08.2024 10:35

вам следует отнести это на codereview.stackexchange.com, я могу отметить одну вещь: ваш класс учетной записи больше похож на конечный автомат, возможно, вам стоит прочитать о конечных автоматах.

Ahmed AEK 02.08.2024 10:36
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете переместить эти функции/условия в класс. Таким образом, вместо длинного условия в 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)
Caleth 02.08.2024 10:24

Возможно, но в исходном вопросе не так уж много контекста — это не «исполняемый» код. Однако я добавлю это к своему ответу.

Joris Timmermans 02.08.2024 10:40
int roundNumber единственная функция — определить, может ли игрок удвоить ставку или нет, так что да, я мог бы заменить ее, но я добавил ее на случай появления новых функций. Я знаю, что такое ООП, но у меня не было возможности понять, где это реализовать, и вы это прекрасно объяснили.
dheb 02.08.2024 11:40

@dheb Рад помочь! Если это достаточно ответило на ваш вопрос, вы тоже можете «принять» ответ.

Joris Timmermans 02.08.2024 11:47

Другие вопросы по теме