Нелогично: круг логики, в рубине используется супер
Я продолжаю пытаться рассуждать о функциональности метода в подклассе, который наследует функциональность родительского класса. Но, похоже, я продолжаю зацикливаться на мысли: один не может вести себя без другого, а другой не может прийти раньше, чем один... Мой мозг болит...
Хорошо, вот мой соответствующий код в родительском классе
class BankAccount
# method to initialize and other methods etc...
def withdraw(amount)
if (amount <= @balance)
@balance -= amount
else
'Insufficient funds'
end
end
end
И вот мой соответствующий код в подклассе
class CheckingAccount < BankAccount
# methods to initialize and other methods etc...
def withdraw
super
end
end
Согласно учебному пособию, из которого я учусь - я пытаюсь достичь
- "Методы CheckingAccount #withdraw увеличивают число" number_of_withdrawals "на единицу после успешного снятия"
Так что, если я создам переменную number_of_withdrawals
внутри моего BankAccount
класс (как подсказывают учебные примеры), то как получается, что когда я звоню super
из версии подкласса withdraw
что он будет знать, чтобы увеличить number_of_withdrawals
на основе if
else
Заявление о выполнении отзыва или нет.
Не должна переменная number_of_withdrawals
быть объявленным в BankAccount
класс, а не CheckingAccount
класс (хотя учебные примеры намекают на то, чтобы поместить его в CheckingAccount
учебный класс). Для полного представления об этом ниже приведена суть тестовых спецификаций () ниже моего текущего состояния кода:
Спецификации теста / Попытка кода
Если кто-то может предоставить рабочий пример
- "Методы CheckingAccount #withdraw увеличивают число" number_of_withdrawals "на единицу после успешного снятия"
С измененным кодом, который я предоставил в GIST - я был бы очень признателен. Я очень новичок в рубине.(1 неделя)
4 ответа
С наследованием родитель может считаться "шаблоном" для ребенка. То есть вы можете вместо того, чтобы вообще использовать родителя, просто записать все в дочерний класс (не то, что вы должны). Дело в том, что все из родительского класса может рассматриваться как копирование в дочерний класс, поэтому, если вы создадите переменную экземпляра для родителя и измените ее из дочернего, будет определена только одна переменная экземпляра, поскольку существует только один объект создан. Другими словами, когда вы говорите CheckingAccount.new
нет отдельного BankAccount
получить экземпляр - это все тот же объект.
Итак, для каждого метода, определенного как в родительском, так и в дочернем методах, необходимо вызвать super
иначе родительский метод не будет вызван. Вот пример с вашим кодом:
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)
С кодом, как он написан в настоящее время, CheckingAccount#withdraw
может проверить возвращаемое значение super
определить, был ли вывод успешным или нет.
Например:
def withdraw(n)
result = super
if result != 'Insufficient funds'
@number_of_withdrawals += 1
end
result
end
Ваш метод делает слишком много и усваивает слишком много предположений. Лучший подход к этому - немного разбить вещи:
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
Поведение, которое вы видите или ожидаете, чтобы выполнить упражнение, связано с динамичной природой Руби. Поскольку ваша программа "интерпретируется" во время ее выполнения (и может быть изменена), Ruby не может знать, что рассматриваемая переменная экземпляра не будет существовать до тех пор, пока метод не будет фактически выполнен.
Вот надуманный пример, который (надеюсь) демонстрирует, почему вы видите / надеетесь увидеть это поведение:
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!
Вам следует взглянуть на этот вопрос и его ответы для более подробного обсуждения различия между статическими / динамическими языками и ранним / поздним связыванием значений.