Передача объектов и передовой опыт объектно-ориентированного проектирования - PullRequest
3 голосов
/ 14 июля 2010

Я студент, изучающий программирование в университете. Для практики я пишу программу для блэкджека. Я использую C ++ и делаю объектно-ориентированный подход к этому.

У меня есть класс Deck, который в основном строит и перетасовывает колоду карт. Сгенерированная колода состоит из массива из 52 объектов класса Card. Это то, что у меня есть.

Мой план состоит в том, чтобы иметь объект «Дилер», у которого колода из 52 карт сдает карту второму объекту «Игрок», а затем раздает собственную руку дилера.

Мой первый вопрос: Разве плохая практика - делать массив объектов Card общедоступным в классе Deck?

Я спрашиваю об этом, потому что считаю массив атрибутом, и меня учили, что большинство атрибутов следует делать закрытыми. Я не хочу начинать использовать плохие или ленивые практики в своих проектах и ​​хочу делать это правильно.

Другой вопрос: Как объекты, такие как объект «Карта», используемые в моей программе «Блэкджек», обычно перемещаются из объекта, такого как дилер, во второй объект, такой как игрок?

Ответы [ 4 ]

3 голосов
/ 14 июля 2010

Мой первый вопрос: является ли плохой практикой делать массив объектов Card общедоступным в классе Deck?

Да.В общем, члены данных всегда должны быть приватными.Полезно ООП создать интерфейс без ассоциированных данных, который определяет, какие операции могут быть выполнены над объектом, а затем предоставить конкретный класс, реализующий этот интерфейс.Данные - это детали реализации, которые не должны быть видны в интерфейсе или даже в полностью конкретном классе.В качестве примера того, почему это плохо, вы можете реализовать свой класс с помощью массива объектов Card прямо сейчас, но, возможно, позже вы решите использовать набор битов, где один бит указывает, присутствует ли карта в колоде или нет.Если вы сделаете объект массива Card общедоступным, изменение представления таким образом нарушит работу других пользователей вашего класса;однако, если вы оставите его закрытым, вы можете внести это изменение, не влияя на пользователей вашего класса.

Другой вопрос: как обычно перемещаются объекты, такие как объект Card, использованный в моей программе для игры в блэкджек?из объекта, подобного дилеру, ко второму объекту, например игроку?

Это зависит от того, нужен ли другому объекту доступ к исходному объекту карты, будет ли другой объект удерживаться на оригиналеобъект в течение длительного времени или только в течение короткого времени, или если другой объект может обрабатывать только копию карты.Это также зависит от того, является ли карта конкретным классом или полиморфным типом, поскольку полиморфные объекты могут быть переданы только по указателю или по ссылке (поскольку передача полиморфных объектов по значению приведет к нарезке кода).С конкретными объектами у вас есть возможность передать копию, если вам не нужно изменить или получить доступ к исходному объекту, и в этом случае требуется ссылка.Выбор правильного способа передачи объектов несколько сложен, но, надеюсь, это прояснит:

Передача по значению, если:

Это примитивный тип или небольшой, неполиморфный конкретный тип, который ненеобходимо изменить.

Передать по константной ссылке - то есть const T& для некоторого типа T - если:

  1. Вам не нужно изменять исходный объект.
  2. Вам не нужно читать исходный объект вне области действия функции.
  3. Вам не нужно читать объект вне области действия функции, или тип не является-полиморфный и дешевый для копирования, поэтому вы можете создать копию, если вам нужно повесить его.

Передача по ссылке - это T& для некоторого типа T - если:

  1. Вам необходимо изменить исходный объект.
  2. Вам не нужно читать / писать оригинальный объект вне области действия функции.
  3. Вы делаетене нужно читать объект за рамками функции или типОн не полиморфный и дешевый для копирования, поэтому вы можете создать копию, если вам нужно повесить ее.

Передача константным умным указателем на const - это const shared_ptr<const T>& для некоторого типа T - если:

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

Передача с помощью постоянного интеллектуального указателя - то есть const shared_ptr<T>& для некоторого типа T - если:

  1. Вам необходимо прочитать и написатьоригинальный объект как в рамках функции, так и за ее пределами.

Я дал каждому из вышеперечисленных в преднамеренном порядке; Вы должны попробовать первый, который будет достаточен для работы, переходя к следующему, только если предыдущего недостаточно. Кроме того, я должен добавить, что boost :: call_traits :: param_type может помочь вам выбрать между передачей по значению и передачей по константной ссылке в случае конкретных неполиморфных типов (это можно определить на основе по размеру объекта, лучше ли передать по значению или передать по постоянной ссылке).

1 голос
/ 14 июля 2010

По крайней мере, ИМО, вы пытаетесь переусердствовать. На самом деле, колода карт не имеет никакого поведения - это просто набор карт. У вас нет дилера, который приказывает колоду карт перетасовать; у вас есть дилер, который перетасовывает колоду. Я сделал бы то же самое в программе - колода была бы std::vector<card>, принадлежащей дилеру (и почти наверняка она должна быть частной).

Для раздачи у каждого игрока есть своя std::vector<card> для своей руки. После этого дилер передавал каждому игроку по одной карте за раз, вызывая функцию-член игрока deal (или любую другую).

0 голосов
/ 14 июля 2010

Мой первый вопрос: является ли плохой практикой делать массив объектов Card общедоступным в классе Deck?

Зависит. Но обычно плохо обнародовать данные.
Это потому, что открытые элементы становятся частью интерфейса и, следовательно, должны поддерживаться.

Было бы лучше сделать массив закрытым членом, а затем выставлять действия через открытый интерфейс. Это позволит вам позже изменить личные данные (например, когда вы узнаете, как использовать вектор, вы можете заменить массив на вектор. Если бы массив был общедоступным, вы не смогли бы изменить тип, не затрагивая любой другой тип). который использовал тот факт, что это был массив).

Принцип: Скрыть детали реализации.
Это приводит к ослаблению связи между типами.

Другой вопрос: как объекты, такие как объект «Карта», использованный в моей программе блэкджек, обычно перемещаются из объекта, например, дилера, во второй объект, например, в игрока?

Удалите его из массива в одном объекте (и уменьшите массив, чтобы показать, что в нем меньше карточек (поэтому вы можете захотеть тип контейнера, который может измениться в размере)) Затем поместите его в другой массив (контейнер) в целевом объекте .

0 голосов
/ 14 июля 2010

1) Как правило, да. Концептуально экземпляры Player не связываются с картами, принадлежащими дилеру, поэтому он должен быть закрытым.

2) Один из способов сделать это:

struct Card
{
    Suit suit;
    Rank rank;
};

class Player
{
private:
    void AddCard(Card card);
    friend class Dealer;
};

class Dealer : public Player
{
public:
    void DealTo(Player& player);
};

Dealer dealer;
Player player2;
dealer.DealTo(player2);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...