Я пытаюсь подобрать C ++. Все шло хорошо, пока моя «тренировочная» программа не затронула меня. Эта загвоздка, я полагаю, проистекает из проблемы дизайна.
Подумайте о Блэкджеке (21). Я сделал несколько уроков.
Колода состоит из — для простоты — имеет множество карт.
-Это может показать все это карты
-Это может перемешать
-Это может удалить карты
Рука — это колода — с пользой
-Он может рассчитать ценность своей руки
-Это может добавить карты в руку
Теперь перейдем к моей проблеме — дизайн плеера
-У игрока есть рука (личный доступ)
Моя проблема с игроком состоит в том, что в руке есть метод с именем addCardToHand. Я чувствую избыточность / плохой дизайн, если мне нужно создать метод Player с именем addCardToHand (Card c), в котором он вызывает и передает один и тот же метод в руки.
или же
объявите Hand h как общедоступный член и в main () сделайте что-то вроде
Игрок р;
Карта aCard;
p.h.addCard (Acard);
Любой совет будет поучительным и высоко ценится. Имейте в виду, я учусь.
Лучший ответ здесь: это зависит 🙂 Хотя я попытаюсь немного уточнить это.
Первый вопрос: имеет ли класс Player какую-либо внутреннюю логику? Если это простой контейнер для Hand, я бы просто написал Player.GetHand().AddCard()
потому что нет причин дублировать код внутри Player.AddCard()
метод, и проблема решена.
Давайте теперь предположим, что там является необходимость реализации дополнительной логики для добавления карты в руку игрока. Это означает, что дополнительный код в классе Player должен вызываться при добавлении карты в руку. В таком случае я вижу три возможных решения.
(Источники только для демонстрационных целей, могут не компилироваться)
Ограничьте доступ к Hand, чтобы никто не мог получить его из Player. Плеер должен будет реализовать такие методы, как AddToHand, RemoveFromHand и т. Д. Выполнимо, но неудобно в использовании.
class Player
{
private:
Hand hand;
public:
void AddToHand(Card & card)
{
hand.Add(card);
}
};
Использовать схема наблюдателя. Когда пользователь (пользователь класса) вызывает Player.GetHand (). AddCard (), Рука уведомляет игрока, что данные изменились, и игрок может действовать соответственно. Вы можете легко добиться этого, используя std :: function из C ++ 11 для реализации событий.
class Deck
{
private:
std::function<void(void)> cardsChanged;
public:
void Add(Card card)
{
// Add a card
if (!(cardsChanged._Empty()))
cardsChanged();
}
void SetCardsChangedHandler(std::function<void(void)> newHandler)
{
cardsChanged = newHandler;
}
};
// (...)
class Player
{
private:
Hand hand;
void CardsChanged() { ... }
(...)
public:
Player()
{
hand.SetCardsChangedHandler([&this]() { this.CardsChanged(); } );
}
};
Определите интерфейс IHand со всеми необходимыми методами интерфейса. Очевидно, что Hand должен реализовывать IHand, а Player.GetHand () должен возвращать IHand. Хитрость в том, что IHand, возвращаемый Player, не обязательно должен быть экземпляром Hand, но вместо этого он может быть декоратором, действующим как мост между пользователем и реальным экземпляром Hand (см. шаблон декоратора).
class IHand
{
public:
virtual void Add(Card card) = 0;
virtual void Remove(Card card) = 0;
};
class Hand : public IHand
{
// Implementations
}
class PlayersHand : public IHand
{
private:
Hand & hand;
Player & player;
public:
PlayersHand(Hand & newHand, Player & newPlayer)
{
hand = newHand;
player = newPlayer;
}
void Add(Card card)
{
hand.Add(card);
player.HandChanged();
}
// ...
};
class Player
{
private:
Hand hand;
PlayersHand * playersHand;
public:
Player()
{
playersHand = new PlayersHand(hand, this);
}
IHand GetHand()
{
return playersHand;
}
}
Лично, во втором случае я бы выбрал второе решение — оно достаточно простое и его легко расширять и повторно использовать в случае дальнейших потребностей.
Функция переадресации вызовов является обычной практикой. Вы должны думать об этом как о добавлении некоторого уровня абстракции. Это опять не то же самое (что означало бы избыточность), а реализация одного метода с использованием другого.
Вы можете представить некоторые модификации в будущем, например, добавление Player
кеш карт или другие вещи, которые необходимо обновить при вызове пользователя addCardToHand
, Куда бы вы добавили код обновления кэша, если бы не реализовали метод пересылки?
Также обратите внимание, что «интерфейс» Player::addCardToHand
не должен совпадать с Card::addCard
то есть аргументы и возвращаемое значение могут быть разными в этих функциях. Может быть, в этом случае это не так важно, но, как правило, функция пересылки — это место, где происходит перевод Player
интерфейс и Hand
Интерфейс может быть добавлен.