Ruby 私有实例变量,有例外
Posted
技术标签:
【中文标题】Ruby 私有实例变量,有例外【英文标题】:Ruby private instance variables, with exceptions 【发布时间】:2011-01-23 13:54:57 【问题描述】:我正在用 ruby 制作纸牌游戏。
我有 Game 类,它有一个 Player 对象数组。
array_of_players = Array[
Player.new("Ben"),
Player.new("Adam"),
Player.new("Peter"),
Player.new("Fred"),
]
my_game = Game.new(array_of_players)
puts my_game.players[2].name #=> Peter
每个玩家也可以访问游戏,这样他们就可以像这样访问游戏的重要部分
self.game.last_card_dealt
每个玩家也有卡片(Player.cards),我想确保玩家不能访问彼此的卡片。但是,游戏确实需要访问卡牌,所以我认为使用private
不合适,而且玩家需要访问彼此的一些信息,所以我不希望使用private
要么……
基本上,我希望这些工作。
self.cards #where self is a Player object
self.players[0].cards #where self is the Game
self.game.players[0].name #where self is a Player object
这失败了:
self.hand.players[0].cards #=> Nice try sucker! Cheating is for losers.
如何处理像这样更复杂的权限? 谢谢。
【问题讨论】:
应用程序(甚至用户)角度的隐私概念不应与 OO 术语中的隐私混淆。我认为在这种情况下依赖 OO 隐私概念是完全错误的,您应该通过应用程序逻辑来确保这一点。 【参考方案1】:感谢您的所有回复。
最后我想我可以给授权对象一个密钥,用于允许它访问方法的核心。
游戏对象有@auth_object并将其设置为它打算访问其秘密方法的玩家对象,玩家秘密方法检查hand.auth_object是否为self
,否则它什么也不做。然后@auth_object 被设置回零。 @auth_object 有 attr_reader 但没有 writer。
这行得通。
【讨论】:
【参考方案2】:这比我的其他答案更实用,并使用 Game 对象作为游戏本身中所有信息(玩家、卡片等)的代表。请注意,您仍然必须相信调用者会通过自己,但认真地划定界限在哪里?
class Player
attr_reader :name
def initialize(name)
@name = name
end
end
class Cards
attr_accessor :cards
end
class Game
attr_reader :name, :players
def initialize(players)
@name = "Game Master"
@hands = []
@players = players.each do |p|
puts "Added %s to game." % p.name
@hands << :player => p, :cards => Cards.new
end
end
def view_hand(player, caller)
@hands.each do |hand|
if hand[:player] == player
if hand[:player] == caller or caller == self
puts "%s: You can access all these cards: %s" % [caller.name, hand[:cards]]
else
# Do something to only display limited cards depending on this caller's view capabilities
puts "%s: You can only access the cards I will let you see: %s" % [caller.name, hand[:cards]]
end
end
end
end
def my_cards(player)
@hands.each do |hand|
puts "%s's cards: %s" % [player.name, hand[:cards]] if hand[:player] == player
end
end
end
g = Game.new([Player.new('Bob'), Player.new('Ben')])
puts "\nCalling each Player's cards as each Player:\n\n"
g.players.each do |gp|
g.players.each do |p|
g.view_hand(gp, p)
end
end
puts "\nCalling each Player's cards as Game:\n\n"
g.players.each do |p|
g.view_hand(p, g)
end
puts "\nEach Player calls for their own cards:\n\n"
g.players.each do |p|
g.my_cards(p)
end
输出:
Added Bob to game.
Added Ben to game.
Calling each Player's cards as each Player:
Bob: You can access all these cards: #<Cards:0x100121c58>
Ben: You can only access the cards I will let you see: #<Cards:0x100121c58>
Bob: You can only access the cards I will let you see: #<Cards:0x100121bb8>
Ben: You can access all these cards: #<Cards:0x100121bb8>
Calling each Player's cards as Game:
Game Master: You can access all these cards: #<Cards:0x100121c58>
Game Master: You can access all these cards: #<Cards:0x100121bb8>
Each Player calls for their own cards:
Bob's cards: #<Cards:0x100121c58>
Ben's cards: #<Cards:0x100121bb8>
【讨论】:
【参考方案3】:这玩起来很有趣。我不确定这是否是最好的答案,但它确实有效。关键是将调用对象传递给 Player.cards(obj),并检查它是 Player 本身还是 Game 类型,两者都具有合法访问权限。
class Player
attr_accessor :name, :game
attr_writer :cards
def initialize(name)
@name = name
@game = nil
@cards = nil
end
def cards(caller)
puts "%s cards called by %s." % [self, caller]
if caller.kind_of?(Game) or caller == self
puts "Here's your cards %s." % @cards
else
puts "Nice try sucker! Cheating is for losers."
end
end
end
class Cards
def initialize
@cards = [1, 2, 3]
end
end
class Game
attr_reader :players
def initialize(players)
@players = players.each do |p|
puts "Added %s to game." % p.name
p.game = self
p.cards = Cards.new
end
end
end
g = Game.new([Player.new('Bob'), Player.new('Ben')])
puts "\nCalling each Player's cards as each Player:\n\n"
g.players.each do |gp|
g.players.each do |p|
p.cards(gp)
end
end
puts "\nCalling each Player's cards as Game:\n\n"
g.players.each do |p|
p.cards(g)
end
还有输出:
Added Bob to game.
Added Ben to game.
Calling each Player's cards as each Player:
#<Player:0x100122b30> cards called by #<Player:0x100122b30>.
Here's your cards #<Cards:0x1001229c8>.
#<Player:0x100122ae0> cards called by #<Player:0x100122b30>.
Nice try sucker! Cheating is for losers.
#<Player:0x100122b30> cards called by #<Player:0x100122ae0>.
Nice try sucker! Cheating is for losers.
#<Player:0x100122ae0> cards called by #<Player:0x100122ae0>.
Here's your cards #<Cards:0x100122928>.
Calling each Player's cards as Game:
#<Player:0x100122b30> cards called by #<Game:0x100122ab8>.
Here's your cards #<Cards:0x1001229c8>.
#<Player:0x100122ae0> cards called by #<Game:0x100122ab8>.
Here's your cards #<Cards:0x100122928>.
【讨论】:
感谢您的回复。我想知道如何阻止恶意玩家偷偷传入他们正在调用 .cards 的玩家的玩家对象?我们不是回到第一方吗?例如。 self.game.player[0].cards(self.game.player[0]) 首先我要说的是,我相信 Mladen 的上述评论完全适用于这种思路。不过,我也觉得把这些东西拼出来很有趣(也许这里的拼图是你的目标,而不是它的实用性?)。在这种情况下,我会说,您需要所有查询通过公共接口方法通过 Game(我们选择的委托人)进行路由,并根据调用者拥有的权限交回所有信息。 (即用户只能通过查询 Game 来获取游戏信息、自己的牌、对手的名字和对手可见的牌)。【参考方案4】:保持Game.player
为私有,以禁止玩家通过数组访问其他玩家。
例如,当self
是玩家时,self.game.players[0].name
有点傻。
也许您想要一个只返回玩家姓名数组的公共 Game.player_names
方法?
除此之外,您还可以公开Players.opponents
方法。
示例
Game.player_names
class Game
# ...
def player_names
self.players.collect |p| p.name
end
private
# private game methods
end
玩家.对手
class Player
# ...
def opponents(i=nil)
return i.nil? ? self.game.player_names : self.game.player_names[i]
end
end
【讨论】:
以上是关于Ruby 私有实例变量,有例外的主要内容,如果未能解决你的问题,请参考以下文章