【问题标题】:Use a method from one class within method from another class?在另一个类的方法中使用一个类的方法?
【发布时间】:2011-06-17 17:31:44
【问题描述】:

我创建这个游戏的目的是为了学习 OOP,但我在其中的一部分遇到了问题。以下是导致我出现问题的原因:

我有两节课。在 Player 类的第 3 行,我有一些代码可能是错误的,但基本上,我想要做的是使用盔甲来修改玩家受到的伤害。但是,我收到一个错误:“nil:NilClass (NoMethodError) 的未定义方法'保护'

我有盔甲作为另一个职业。我认为这个问题可能与我在 Armor 中提到保护并且在 Player 中提到 @armor 时调用 @armor.protection 的事实有关,但我不确定如何解决这个问题。我已经添加了我认为与下面我的问题相关的所有代码。就像我说的,我在这方面真的很陌生,所以请使用菜鸟能理解的术语。

class Player
  def equip(armor)
    @armor = armor
  end

  def hit(damage)
    #damage = damage - @armor.protection
    @health -= damage
  end
end  

class Armor
  def initialize(name, protection)
    @protection = protection
  end
end

编辑:添加了额外的代码来显示我为澄清所做的所有事情。 不过,我不希望任何人阅读我所拥有的所有内容。 :S 这可能很吓人并咆哮起来。 :P

class Player 

  def initialize(name, health) 
    @name = name 
    @health = health 
  end 

  def equip(armor) 
    @armor = armor 
  end 

  def health 
    @health 
  end 

  def health=(value) 
    @health = value 
  end 

  def hit(damage) 
damage = damage - @armor.protection 
    @health -= damage 
  end 

  def dead? 
if @health <= 0 
return true 
elsif @health > 0 
return false 
end 
  end 

  def name 
    @name 
  end 

  def attack(target) 
    damage = rand(30) 
    puts "#{@name} attacks #{target.name}" 
target.hit(damage) 
puts "#{@name} hits #{target.name} for #{damage} damage." 
  end 
end 

class Armor 
  def initialize(name, protection) 
  @protection = protection 
  end 
end 


player1 = Player.new("Melanie", 100) 
player2 = Player.new("a Monster", 200) 
shirt = Armor.new('shirt', 4) 
player1.equip(shirt) 

while player1.dead? == false && player2.dead? == false 
  player1.attack(player2) 
    if player2.health > 0 
      puts "#{player2.name}'s health is at #{player2.health}." 
    elsif player2.health <= 0 
puts "#{player2.name} has no health." 
end 
  player2.attack(player1) 
    if player1.health > 0 
      puts "#{player1.name}'s health is at #{player1.health}." 
    elsif player1.health <= 0 
puts "#{player1.name} has no health." 
end 
end 

if player1.health > player2.health 
  puts "#{player2.name} is dead." 
  puts "#{player1.name} wins." 
elsif player2.health > player1.health 
  puts "#{player1.name} is dead." 
  puts "#{player2.name} wins." 
elsif player2.health == player1.health 
  puts "#{player1.name} and #{player2.name} killed each other." 
end 

【问题讨论】:

    标签: ruby class methods


    【解决方案1】:

    我刚刚运行了您的第二个(完整)示例。

    除了其他答案中解释的访问器问题(只需将attr_reader :protection 添加到Armor 类中),您在测试场景中忽略了一些东西:)

    错误信息给出了提示:undefined method 'protection' for nil:NilClass (NoMethodError)。鉴于这是在hit 方法的第一行引起的,这意味着@armornil,当然nil 不是Armor 的实例,所以它没有protection 方法。为什么是零?好吧,看看你的战斗是如何开始的:

    player1 = Player.new("Melanie", 100) 
    player2 = Player.new("a Monster", 200) 
    shirt = Armor.new('shirt', 4)
    player1.equip(shirt) 
    

    只有梅兰妮有一件衬衫,而你根本没有给怪物穿盔甲!不太公平,是吗:)

    要解决这个问题,您需要给他一些盔甲,或者更改您的 hit 方法,以便在 @armor 未初始化时仍然有效。一个很好的 OO 方法是使用不提供保护的默认虚拟盔甲初始化所有玩家:

    class Player
      def initialize(name, health)
        @armor = Armor.new('nothing', 0)
        # ...
    

    完成!


    现在,由于无论你的游戏的具体规则是什么,这个假盔甲都会很有用,我将从 Player 类的角度抽象它,让 Armor 类负责创建它:

    class Armor
      class << self # define a class method, like new
        def none
          self.new('nothing', 0)
        end
      end
      # ...
    

    那么你可以只说Armor.none 而不是Player.initialize 中的Armor.new('nothing', 0)。这样,如果您需要更改 Armor 在内部的工作方式,您可以同时更新虚拟盔甲,而无需触及其他类。

    【讨论】:

    • 哇!棒极了!太感谢了!我不会认为这是一百万年后的问题。我必须学会更好地阅读错误信息。至少我离“天哪!一个错误!”还有几步之遥!
    • 不客气。顺便说一句,如果我能再挑剔一些,我会看到 while player1.dead? == false &amp;&amp; player2.dead? == false... 笨拙的风格!至少,将some_boolean == false 替换为not some_boolean;我个人会将dead? 谓词替换为alive? 谓词,您可以直接使用而无需否定:)
    • 是的,我朋友就是这么说的!谢谢!
    【解决方案2】:

    试试这个:

    player = Player.new 
    armor = Armor.new('Mythril', 100)
    player = player.equip(armor)  #Initialise the armor object inside Player.
    
    player.hit(10)
    

    【讨论】:

    • 应该是 player = Player.new 和 player.equip(armbor) 和 player.hit(10) ,这些都是实例方法。
    • 我想我已经有了类似的东西。我已经添加了我必须澄清的所有代码。希望这是有道理的。
    【解决方案3】:

    如果您的 Armor 类有 protection 方法,它会正常工作。然而它没有,所以即使你从Armor 类中调用它,你也会得到同样的错误。要定义它,您可以使用attr_readerattr_accessor 或手动定义它。

    class Armor
      attr_accessor :protection
    
      def initialize(name, protection)
        @protection = protection
      end
    end
    

    class Armor
      def initialize(name, protection)
        @protection = protection
      end
    
      def protection
        @protection
      end
    end
    

    【讨论】:

    • 截至目前,程序不知道说盔甲,我的意思是,在这个玩家的例子中,一件衬衫。我唯一提到的盔甲,小写 a,除了伤害 = 伤害 - @armor.protection 是在类 Player 中装备的定义。我尝试了第二个示例,希望它能解决这个问题,但无论哪种情况,我仍然遇到同样的错误。我知道我有点过头了,但我确实了解很多,这绝对是一次学习经历。 :S
    • @Melanie:首先我的代码是错误的,因为当我提到保护时我不小心输入了盔甲,所以它不能工作。现在我修复了它,只要你在调用任何使用@armor 的方法之前调用equip,它就可以工作。
    • 要求首先调用equip 的更好解决方案可能是将@armor 初始化为Playerinitialize 方法中的零输出Armor 类实例。这样一来,如果玩家一开始没有盔甲,您就不必担心。
    • 仅供参考和谷歌素材:@Mike Bethany 的建议是Null Object Refactoring
    【解决方案4】:

    这里的问题是您有一个@protection 实例变量,但没有“访问器”可以访问它。实例变量对于它们所属的类的实例是私有的,所以如果你想将它们暴露给外部世界,你必须设置访问器来这样做。在这种情况下,您需要:

    class Armor
      attr_reader :protection
      ...
    end
    

    这会让你调用@armor.protection,并返回你的armor实例的@protection变量的值。

    【讨论】:

    • 这绝对帮助我理解了 attr_reader、attr_accessor 的东西,因为我以前从未“理解”过它,但我认为部分问题是我没有将盔甲“绑定”到 Armor 上。我不知道该怎么做。截至目前,该程序不知道说盔甲,我的意思是,在这个玩家的情况下,一件衬衫。我唯一提到的盔甲,小写 a,除了伤害 = 伤害 - @armor.protection 是在类 Player 中装备的定义。 :S
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多