不合逻辑:逻辑循环,在 ruby 中使用 'super'

Illogical: circle of logic, in ruby using 'super'

我一直在尝试根据继承父 class 功能的子 class 中方法的功能进行推理。但似乎我一直陷入这样的心理循环:one 没有 otherother 不能出现在 one[=56 之前=]...脑子疼...

好的,这是我在父级中的相关代码 class

class BankAccount
  # method to initialize and other methods etc...

  def withdraw(amount)
    if (amount <= @balance)
      @balance -= amount
    else
      'Insufficient funds'
    end
  end

end

这是我在子class

中的相关代码
class CheckingAccount < BankAccount
  # methods to initialize and other methods etc...

  def withdraw
    super
  end 

end

根据我正在学习的教程 - 我想要完成的是

  • "CheckingAccount methods #withdraw increments 'number_of_withdrawals' by one after a successful withdrawal"

因此,如果我在我的 BankAccount class 中创建一个变量 number_of_withdrawals(如教程示例所暗示的那样),那么当我从withdraw 的 subclass 版本,它将根据 if else 语句是否执行撤回而知道增加 number_of_withdrawals

不应该在 BankAccount class 中声明变量 number_of_withdrawals,而不是 CheckingAccount class(即使教程示例暗示要将它在 CheckingAccount class 中)。要全面了解这一点,请查看我当前代码状态下方的测试规范 () 的要点:

Test Specs / Code Attempt

如果有人可以提供

的工作示例
  • "CheckingAccount methods #withdraw increments 'number_of_withdrawals' by one after a successful withdrawal"

我在 GIST 中提供了经过修改的代码 - 我将非常感激。我对 ruby 很陌生。(1 周)

使用目前编写的代码,CheckingAccount#withdraw 可以检查 super 的 return 值来确定提款是否成功。

例如:

def withdraw(n)
  result = super
  if result != 'Insufficient funds'
    @number_of_withdrawals += 1
  end
  result
end

您看到的行为——或预期为完成练习而提供的行为——是由于 Ruby 的 dynamic nature。由于您的程序是 "interpreted",因为它是 运行(并且可能会发生变化),因此 Ruby 无法知道所讨论的实例变量将不存在,直到该方法实际上是已执行。

这是一个人为的例子,它(希望)展示了为什么你 seeing/hoping 看到这种行为:

class Foo    
  def say_something_that_doesnt_exist
    # Foo is free to try to make use of @nothing, 
    # in case it's been provided by a child class' 
    # instance, but if it's not present, its value 
    # will just be nil
    puts "say_something_that_doesnt_exist, like #{@nothing}!"
  end

  def say_something_that_does_exist
    puts "say_something_that_does_exist, like #{@bar}!"
  end
end

class Bar < Foo
  attr_reader :bar

  def initialize
    super
    @bar = "bar"
  end
end

bar = Bar.new
bar.say_something_that_doesnt_exist # say_something_that_doesnt_exist, like !
bar.say_something_that_does_exist # say_something_that_does_exist, like bar!

您应该查看 this 问题及其答案,以更详细地讨论 static/dynamic 语言与 early/late 值绑定之间的区别。

你的方法做的太多了,内化了太多的假设。一个更好的方法是稍微分解一下:

class BankAccount
  attr_reader :balance

  def initialize
    @balance = 0
  end

  def withdraw(amount)
    if (can_withdraw?(amount))
      credit(amount)

      after_withdraw
    else
      false
    end
  end

  def can_withdraw?(amount)
    amount <= balance
  end

  def after_withdraw
    # No default behaviour
  end

  def debit(amount)
    @balance += amount
  end

  def credit(amount)
    @balance -= amount
  end
end

然后你可以让子类专门化非常具体的方法,而不必如此努力地依赖 super:

class CheckingAccount < BankAccount
  attr_reader :overdraft

  def initialize
    super
    @overdraft = 0
    @withdrawals = 0
  end

  def can_withdraw?(amount)
    amount <= balance + overdraft
  end

  def after_withdraw
    @withdrawals += 1
  end
end

通过继承,parent 可以被认为是 child 的 'template'。也就是说,您完全可以不使用 parent 而只是将所有内容写入 child class(不是您应该这样做)。关键是 parent class 中的所有内容都可以被认为是被复制到 child class 上,所以如果你在 [=35] 上创建一个实例变量=] 并将其从 child 更改为,只定义了一个实例变量,因为只有一个 object 被实例化。换句话说,当你说 CheckingAccount.new 时,没有 separate BankAccount 被实例化——它们都是一样的 object。

因此,对于在 parent 和 child 中定义的每个方法,您需要调用 super 否则 parent 方法不会被调用。这是您的代码示例:

class BankAccount
  def initialize
    @balance = 0
    @number_of_withdrawals = 0
  end

  def withdraw(amount)
    if amount <= @balance
      @balance -= amount
      @number_of_withdrawals += 1
    else
      'Insufficient funds'
    end
  end
end

class CheckingAccount < BankAccount
  MAX_FREE_WITHDRAWALS = 3

  def withdraw(amount)
    if @number_of_withdrawals >= self.class::MAX_FREE_WITHDRAWALS
    amount += 5
    super(amount)
  end 

end

我只是浏览了需求文档,所以一定要仔细检查(例如,不要只拿我的代码,然后把它当作作业 :D)