我刚才有一个与设计模式有关的小问题。
考虑可以容纳Card对象的Player对象。
Player player1;
Player player2;
Player dealer;
玩家可以互相给卡。是否有一种OO方法来设计处理此问题的方法?
player1.giveCard(Card, player2);
玩家1可以利用其他玩家的方法似乎不对。有什么想法吗?例如,是否所有玩家都有一个名为getCard的方法?
Player::giveCard(Card card, Player player) {
player.getCard(card)
}
答案 0 :(得分:1)
面向对象方法的美妙之处在于,有无限的方法来抽象和设计相同的概念。问题是您想要选择的。
似乎在这个特定的实例中,您对所描述的抽象的关注是player1需要使用player2的方法来实现目标。但是,我想改变你对方法的看法。
首先,方法应该被认为是我们与世界的公共接口。另一方面,属性是我们保密并隐藏在对象/头部/身体/等内的私人物品。
确实,许多编程语言都有私有和受保护的方法,仅供内部使用,但这只是为了清理代码,因此我们的公共方法不会长达数百行。
对于您的纸牌游戏,玩家可以通过任何其他对象获得卡片,因此我将定义公共方法Player.giveCard(卡片)。这是一个简单的部分,已经被其他答案所触及。
但问题是,这个方法会发生什么? 另外,如何将卡从原始手中移除?
我们可以在这里做几件事,这个清单并不完整:
在这种情况下,player1选择将card_589提供给player2。因此,player1调用方法player2.giveCard(card_589)。在现实世界中,这将由玩家1物理地拿出卡片供玩家2拿走来证明。如果player2接受该牌,则player1不再拥有该牌,并且必须将其从手中移除。如果player2不接受它,那么player1不会丢失该卡,并将其放回手中。
为了对此进行建模,我们将制定一个简单的规则:giveCard方法返回一个布尔结果来指示player2是否接受了卡片....在player1调用player2.giveCard()之后,他对于player2是否接受了卡,因为这取决于播放器2,在这种情况下。
我们的代码在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。 Player1选择在他交出牌之前将牌从他自己的牌中取出,这样牌就不会同时出现在两个位置。如果Player2没有拿到卡,则player1将其放回到他的牌组中,否则什么都不做,因为该卡现在与player2一起。
就个人而言,这是抽象模型的最简单方法,也是我最喜欢的。
这是我最喜欢的场景,当我们对具有某种延迟的游戏进行建模时,例如在计算机网络模拟或国际象棋邮件模拟中。 Player1将卡邮寄给player2,但是player2可能会或可能不会收到该卡。在这个游戏中,让我们假设你有一张桌子,就像扑克桌一样,任何玩家都可以在自己和另一个玩家之间放一张牌,这样只有其他玩家可以到达该牌。
对于这种情况,我们将创建一个名为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..*]
这引入了安全问题,因为我告诉Table,只有userId可以拿起卡,只有myId可以验证和检索卡,所以Table对象需要一些方法来验证我("当前对象")有权访问由" userId"标识的表格上的位置。和#34; 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之间的隧道。 Player1通过识别他目前没有办法将牌给玩家2来建立隧道,因此他创建了一个隧道。他给了player2一个隧道的副本,拿着"另一个结束"然后播放器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 );
}
这个选项很奇怪,但它给了我们很大的灵活性。在上面的示例中,Dealer和Floor甚至不是iCardHolder接口的实现者,但是他们持有对实现该接口的对象的引用,因此他们仍然可以使用该卡。
在使用iCardHolder的每个实现中,这与其他实现完全不同,代码非常简单,因为我们已经卸载了卡位置的操作并将这个责任放在卡本身上,并且所有卡都关心是与它交互的对象,同意各种合同并承诺实现一个removeCard方法和一个addCard方法。作为安全性,卡片会将当前所有者的副本保存在自己的内存中,这样如果其中一个CardHolders出现错误,则卡片本身会保留其当前所有者的答案。
长话短说
没有一种正确的方法来模拟你的游戏。这完全取决于个人偏好以及您希望系统如何表现。成为一名程序员是件好事。作为进行代码设计的人,您可以设置程序运行的规则,什么是良好的对象交互,以及什么是不良的对象交互。
答案 1 :(得分:0)
你需要知道这张卡来自哪个播放器吗?
另外,我假设一个名为CardGame的对象将成为所有Player对象的持有者,并且会对玩家进行所有调用,使得一个Player对象不会改变另一个Player对象的状态,但是CardGame对象完成所有这些。
因此,在CardGame对象中,您可以进行如下调用:
player1.giveCard(Card); player2.receiveCard(卡);
答案 2 :(得分:0)
我会说玩家有一手牌,而Hand.addCard(牌)和Hand.removeCard(牌)就是在这里实施的方法。
游戏将处理从一个玩家移除一张牌并将其交给下一个(通过那些手牌),因为游戏知道所有玩家对象,但玩家对象不一定需要彼此了解。 / p>