Прекрасная вещь в объектно-ориентированных подходах состоит в том, что существуют бесконечные способы абстрагирования и разработки одной и той же концепции.Вопрос в том, что вы хотели бы выбрать.
Похоже, что в данном конкретном случае ваша озабоченность абстракцией, которую вы описали, заключается в том, что player1 должен использовать методы player2 для достижения цели.Тем не менее, я бы хотел изменить ваше представление о методах.
Вообще, методы следует рассматривать, во-первых, как наши общедоступные интерфейсы с миром.С другой стороны, свойства - это те личные вещи, которые мы храним в тайне и закрываем внутри наших объектов / голов / тел / и т. Д.
Это правда, что многие языки программирования имеют частные и защищенные методы, которые предназначенытолько для внутреннего использования, но на самом деле это просто для очистки кода, поэтому наши общедоступные методы не имеют длины в сотни строк.
Для вашей карточной игры игрок может получить карту от любого другого объекта, поэтомуЯ бы определил публичный метод Player.giveCard (Card).Это простая часть, которая уже затрагивалась другими ответами.
Но возникает вопрос, что происходит внутри этого метода?Кроме того, как извлечь карту из исходной руки?
Здесь мы можем сделать несколько вещей, и этот список ни в коем случае не полон:
- Игрок может взаимодействовать только сдругой игрок.
В этой ситуации игрок 1 решает отдать карту_589 игроку 2.Итак, player1 вызывает метод player2.giveCard (card_589).В реальном мире это будет продемонстрировано игроком 1, физически протягивающим карту, чтобы игрок 2 мог ее взять.Если игрок2 принимает карту, игрок1 больше не имеет ее и должен удалить ее из своей руки.Если player2 не принимает его, то player1 не теряет карту и кладет ее обратно в свою руку.
Чтобы смоделировать это, мы должны сделать одно простое правило: метод giveCard возвращает логический результат, чтобы указать,player2 берет карту .... После того, как player1 вызывает player2.giveCard (), он не может сказать, берет ли player2 карту, потому что в этом сценарии это зависит от player2.
Наш код может выглядеть следующим образомэто где-то внутри функций player1:
//begin pseudocode to give card to player identified by player2
//let self refer to player1's own methods
Player{
public giveCardToPlayer( player2, card_id ){
card = self.removeCardFromHand( card_id );
cardTaken = player2.giveCard( card );
if( cardTaken === false ){
self.addCardToHand( card );
}
}
//in the game management system, or elsewhere in player1's code, you then write
player1.giveCardToPlayer( player2, card_587 );
//or, if in another method "player1.play()", for example:
//self.giveCardToPlayer( player2, card_587 )
//
//end pseudocode
Это самое простое решение.Здесь player1 не видит ничего в принятии решения player2 относительно того, будет ли взята card1.Игрок 1 решает удалить карту из своей руки, прежде чем передать ее, чтобы карта не находилась сразу в двух местах.Если Player2 не берет карту, player1 кладет ее обратно в свою колоду, но в противном случае ничего не делает, потому что карта теперь с player2.
Лично это самый простой способ абстрагировать модель, и мой любимый.
Игрок может взаимодействовать через какого-то посредника
Это мой любимый сценарий, когда мы моделируем игру с некоторой задержкой, такой как симуляция компьютерной сети или шахматы по симуляции почты.,Player1 отправляет карту игроку 2, но игрок 2 может или не может получить карту.В этой игре давайте предположим, что у вас есть стол, например покерный стол, и любой игрок может положить карту между собой и другим игроком, чтобы только этот другой игрок мог достать карту.
Для этого сценария,мы создадим новый объект с именем Table, и хотя существует множество способов абстрагировать размещение карты на столе, я выберу этот метод в качестве общедоступного интерфейса для действия:
Table.placeCardForUser( card, userId, myId, securityToken ) : bool
Table.countCardsOnTableToUserFromUser( userId, myId, securityToken ) : int
Table.pickUpCardToUser( userId, myId, securityToken ) : Card[0..*]
Table.pickUpCardsToMe( myId, securityToken ) : Card[0..*]
Это создает проблемы безопасности, потому что я говорю Таблице, что только userId может забрать карточку, и только myId может проверить и извлечь карточку, поэтому объекту Table нужен какой-то способ проверить, что I («текущий объект»)) имеют право доступа к месту на таблице, указанному в «userId» и «myId», но есть и множество решений для этого.
//begin psuedocode for current example
//we are in player1's function body
card = self.removeCardFromHand( card_587 );
player2_id = self.identifyPlayerToReceive( card );
table.placeCardForUser( card, player2_id, myId, securityToken );
//end current action
//at next opportunity to act, check to see
//if card was taken
cardCount = table.countCardsOnTableToUserFromUser( userId, myId, securityToken );
if( cardCount > 1 ){
//player2 has not taken card or other cards that have accumulated
pickUpCards = self.decideToPickUpCardsToPlayer( player2_id );
if( pickUpCards === true ){
cards = table.pickUpCardToUser( player2_id, myId, securityToken );
foreach( cards as card ){
self.addToHand( card );
}
}
}
//now check to see if anyone has given me cards between last round and this round
cards = table.pickUpCardsToMe( myId, securityToken );
foreach( cards as card ){
//let us assume that player1 takes all cards given to him
self.addToHand( card );
}
Вариации этого могут быть сделаны.Вы можете представить туннель между player1 и player2.Игрок1 устанавливает туннель, признавая, что в настоящее время у него нет способа раздать карты игроку2, и поэтому он создает туннель.Он передает копию туннеля игроку 2, удерживая «другой конец», а игрок 2 также сохраняет копию туннеля.Как и в случае со столом, этот туннель теперь является местом, в котором можно хранить предметы, которые передаются туда и обратно игроку2, но поскольку только у игрока1 и игрока2 есть ссылки или указатели на туннель, только эти два игрока могут помещать предметы втуннель или убрать их, поэтому у нас есть посредник, который не требует такой большой безопасности.Мы можем создать туннели, чтобы связать всех игроков со всеми другими игроками, и это все еще является разновидностью промежуточного дизайна.
Самосознательная карта
Иногда нам нужны конструкции, которые легче кодировать и меньше похожи на реальность.Что произойдет, если код объекта Player забудет удалить объект карты из его руки?Теперь, поскольку объекты обычно передаются по ссылке, каждый игрок player2 и player1 имеет ссылку на карту, а игровой симулятор считает, что существует две копии одной карты!
В этой ситуации мы можем создать картубыть самоосознанным и дать карте доступ к руке игрока.
Для этой абстракции я буду моделировать карту следующим образом:
//begin pseudocode
Card{
private owner;
//this is a private link to the object in which the card lies
//we will allow any object to be the owner of the card, as long
//as the object implements the "CardOwner" interface.
public putInto( newOwner ){
//whoever takes the card must specify a newOwner, which will
//implement the "CardHolder" interface.
success = newOwner.addCard( self );
if( success ){
self.owner.removeCard( self );
self.owner = newOwner;
}
}
}
Затем мы можем определить интерфейс какследует:
//begin psuedocode
iCardHolder{
public removeCard( card ) : bool
public addCard( card ) : bool
}
В этом сценарии мы оторвались от «реальности», предоставив самой карте возможность выполнять действия.Но это полезно в больших проектах, где вы не можете доверять другим программистам помнить подробности о том, как правильно обрабатывать карту.
Предоставляя карте контроль над тем, кто имеет на нее указатели, мы можем гарантировать, чтов любое время существует только одна копия карты, независимо от того, кто ее использует.
Теперь код player1 может выглядеть следующим образом:
//give card to player2
card = self.chooseCard();
player2.giveCard( card );
//put card on the floor
card = self.chooseCard();
floor.giveCard( card );
//put card on the table
card = self.chooseCard();
table.giveCard( card );
И в каждом из этих объектов мыимейте свободу изменять способ получения карты и место ее хранения.
//player2 - is a simple CardHolder
public function giveCard( card ){
myHand = self;
card.putInto( myHand );
}
//the dealer is a cheat and does not implement CardHolder,
//but he has three locations that can act as CardHoldes
//they are:
// dealer.sleave, dealer.hand, and dealer.pocket
public function giveCard( card ){
location = self.chooseCardOwner( [ sleeve, hand, pocket ] );
card.putInto( location );
}
//the floor has random piles that are accumulating
public function giveCard( card ){
pile = self.chooseRandomPile();
card.putInto( pile );
}
Эта опция причудлива, но дает нам большую гибкость.В приведенном выше примере Дилер и Пол даже не являются реализациями интерфейса iCardHolder, но они содержат ссылки на объекты, которые реализуют этот интерфейс, поэтому они все еще могут брать карту.
В каждом из этихРеализации, использующие iCardHolder, который полностью отличается от других, код невероятно прост, потому что мы сняли с себя манипулирование местоположением карты и возложили эту ответственность на саму карту, и все карты заботятся о том, чтобы объекты, которые взаимодействуют сэто, согласитесь на своего рода договор и пообещайте реализовать один метод removeCard и один метод addCard.В целях безопасности карта хранит копию текущего владельца в своей собственной памяти, поэтому в случае ошибки одного из держателей карты сама карта хранит ответ своего текущего владельца.
Long Story Short
Не существует единого правильного способа моделирования вашей игры.Все дело в личных предпочтениях и в том, как вы хотите, чтобы система работала.Это прекрасная вещь быть программистом.Как человек, который делает дизайн кода, вы можете установить правила работы программы, что такое хорошее взаимодействие с объектом и что такое плохое взаимодействие с объектом.