Класс как поле класса. Реализация сеттеров и геттеров
Подскажите, пожалуйста, как правильно воплотить в коде следующую идею. Есть абстрактный класс Item (предмет), от которого наследуются классы Money, Phone, Weapon и так далее. Также есть класс Player (игрок), у которого могут быть перечисленные выше предметы (деньги, телефон, оружие). Я вижу для себя одно решение: сделать классы Money, Phone, Weapon, ... полями класса Player. Однако в таком случае я получаю проблему с использованием сеттеров и геттеров. Например.
class Money : Items {
protected:
int value;
public:
Money() : Money(0) {}
Money(const int value) : value(value) {}
Money(const Money& money) : Money(money.value) {}
void setValue(const int value) { this->value = value; }
int getValue() const { return this->name; }
}
class Player {
protected:
string name;
Money money;
public:
Player() : Player("") {}
Player(const string name) : name(name), Money(0) {}
Player(const Player& player) : Player(player.player) {}
void setName(const string name) { this-> name = name; }
string getName() const { return this->name; }
void setMoney(const int value) { this->money.setValue(value); }
int getMoney() const { return this->money.getValue(); }
}
Если в классе Money произойдут какие-либо изменения (появится еще одно обязательное поле в setter или что-то около того), тогда придется вносить изменения и внутри класса Player. Возможно ли реализовать это как-нибудь более эффективно? Наверняка есть более удачный вариант. Спасибо.
Ответы (1 шт):
Если Ваш класс Player хочет скрыть свои внутренности, то да, придется писать функции, которые будут прослойками для внутренних классов. Но при этом Вы получаете полный контроль над происходящим.
С другой стороны, эти классы можно сделать публичными (или предоставить ссылку на них). И тогда будет чуточку легче.
Как часто решают такую проблему. У класса создают приватные переменные для "этих классов", наследуя их от абстрактного класса (ака "интерфейс" или "базовый класс"), а в публичную часть добавляют функцию, которая возвращает этот самый интерфейс наружу. Казалось бы сложная схема, но по факту, можно легко внутри менять реализацию (класс может как сам создавать внутренние классы, так и получать по ссылке/указателю, так и вообще менять согласно фазе луны/конфигу), тем самым получая большую гибкость.
Например, торчит наружу ссылка/указатель на внутренний класс Money, а нам очень сильно хочется контролировать его. Не проблема, создаем класс-декоратор (обертку), подменяем и готово. Хотим сделать отдельную функцию "добавить денег" в классе игрока - тоже не проблема - у нас же есть интерфейс.
Если в классе Money произойдут какие-либо изменения (появится еще одно обязательное поле в setter или что-то около того)
Половина этой проблемы решается тем, что у нас есть базовый класс, который можно немного улучшить (что бы он мог возвращать все новое, что нужно), а вторая половина решается через "конфиг". То есть, класс Player обязуется в какой то момент вызвать функцию init (конструктор) у своих подопечных классов, передав им класс-конфиг (у меня это к примеру в одном проекте был просто класс с json внутри, в оберточке конечно ), а подопечный класс сам знает, как ему инициализироваться.
Потом у Вас появляться классы "построители" (билдеры), которые по заданным параметрам соберут нужного плеера.
P.S. Пока писал, понял, что хороший пример будет с оружием. К примеру, делаем базовые классы "ручное оружие" (то, которое можно держать в руках), ножное оружие (да, звучит смешно, но дальше будет понятно), добавляем туда функции "стрелять" (а может лучше "использовать"), "перезарядить", "выбросить", "можно ли использовать", а дальше создаем наследников от этого типа - лук-стрелы, шотган, дубина, кулаки (это оружие выбросить сложно), лазер, самоги-скороходы (ножное оружие).
Теперь, когда игрок меняет оружие, мы просто подменяем внутренний указатель и готово. А если мы в руки попытаемся добавить сапоги-скороходы, то получим ошибку компиляции. Возможно, ещё нужно будет создать "оружие-заглушку", что руки не были пустыми (указатель не был nullptr и не нужно было делать проверки постоянные)