关于面向对象方法的美妙的事情是,有无数种方式来抽象和设计相同的概念。问题是你想选择哪一个。
看来,在这种特定情况下,与你所描述的抽象你关注的是,PLAYER1需要使用player2的方法来完成目标。但是,我想改变您对方法的看法。
方法,一般来说,应想到的,首先,我们与世界的公共接口。另一方面,属性是我们保密并锁在我们的物体/头部/身体等内部的那些私人物品。
这是真的,很多编程语言有只用于内部使用私有和保护方法,但其实只是清理代码,以便我们的公共方法并不数百行的。
为了您的卡牌游戏,玩家可以得到一个卡通过任何其他对象,所以我会定义公共方法Player.giveCard(卡)。这是简单的部分,其他答案已经提到了这一点。
但问题变成了,这个方法里面会发生什么? 另外,该卡如何从原来的手中移除?
这里,我们可以做几件事情,而这个名单是不完整:
- 玩家只需与其他玩家互动。
在这种情况下,player1选择将card_589给予player2。所以,player1调用方法player2.giveCard(card_589)。在现实世界中,这将由玩家1身体上持有卡2以供玩家2采用。如果玩家2接受该卡,则玩家1不再拥有该卡,并且必须将其从他的手中移除。如果玩家2不接受它,则玩家1不会松开该卡,并将其放回他的手中。
为了建立这个模型,我们将制定一个简单的规则: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选择在他交出卡之前从他自己的手中取出卡,因此卡不是同时在两个地方。如果玩家2没有拿到该卡,玩家1将其放回他的套牌中,但是否则什么都不做,因为该卡现在与玩家2。
就个人而言,这是抽象模型和我最喜欢的最简单的方法。
- 玩家可以通过交互的一些中介
这是我最喜欢的场景,当我们正在建模一个游戏,有某种延迟,例如在计算机网络仿真,或者通过邮件模拟一个国际象棋。玩家1将卡片邮寄给玩家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..*]
这会带来安全问题,因为我告诉,只有用户id可以拿起卡表,只有身份识别码可以验证和检索卡,所以表格对象需要一些方法来验证我(“当前对象”)有权访问由“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并且因此创建隧道来建立隧道。他将一个隧道的副本给予player2,持有“另一端”,然后player2保留隧道的副本。就像桌子一样,这条隧道现在是一个可以让物品来回传递给玩家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方法。作为安全保障,卡片将当前所有者的副本保留在自己的记忆中,以便如果其中一个卡片持有者出现错误,卡片本身就会为其当前所有者提供答案。
长话短说
还有就是你的游戏模型中没有一个正确的方式。这完全是关于个人偏好,以及你希望系统如何运作。作为一名程序员,这是一件美丽的事情。作为进行代码设计的人员,您可以设置程序运行方式,以及什么是良好的对象交互以及什么是不良对象交互。
你可以做的一件事是为玩家创建getter和setter对象,并保持玩家类以外的逻辑 –