Ответ 1
Я бы сделал это следующим образом:
def filesize
@filesize ||= calculate_filesize
end
private
def calculate_filesize
# ...
end
Поэтому я бы назвал этот метод по-другому, так как я думаю, что это имеет смысл.
Я хочу избежать переоценки значения в вызове метода. До сих пор я делал это:
def some_method
@some_method ||= begin
# lot of code
end
end
Но это заканчивается довольно уродливо. В некотором коде я увидел что-то вроде следующего:
def some_method
@some_method ||= some_method!
end
private
def some_method!
# lot of code
end
Мне не нравится бит (!
) в конце, поэтому я придумал следующее:
def some_method
@some_method ||= _some_method
end
private
def _some_method
# lot of code
end
Я бы сделал это следующим образом:
def filesize
@filesize ||= calculate_filesize
end
private
def calculate_filesize
# ...
end
Поэтому я бы назвал этот метод по-другому, так как я думаю, что это имеет смысл.
Есть еще один способ, более явный стиль Java.
Прежде всего, вы должны реализовать аннотации, такие как аннотации в стиле Java в Ruby "и" Как имитировать Java-подобные аннотации в Ruby?".
Затем вы должны добавить аннотацию как _cacheable, которая будет указывать методу, что она должна возвращать переменную экземпляра, и если она равна null, она должна вычислять ее путем вызова метода, поэтому ваш код будет более понятным:
_cacheable
def some_method
# do_some_work
end
Я использую memoist gem, который позволяет легко запоминать метод без необходимости изменения исходного метода или создания двух методов.
Так, например, вместо двух методов file_size
и calculate_file_size
, и вам нужно реализовать memoization самостоятельно с переменной экземпляра:
def file_size
@file_size ||= calculate_file_size
end
def calculate_file_size
# code to calculate the file size
end
вы можете просто сделать это:
def file_size
# code to calculate the file size
end
memoize :file_size
Каждая memoized функция имеет способ сбросить существующее значение.
object.file_size # returns the memoized value
object.file_size(true) # bypasses the memoized value and rememoizes it
Таким образом, вызов object.file_size(true)
будет эквивалентным вызову object.calculate_file_size
...
Обычно я использую begin, end
в соответствии с вашим первым примером, но если есть еще немного кода, я просто смотрю, существует ли переменная, нет необходимости создавать другой метод для этого.
def some_method
return @some_method if @some_method
# lot of code
@some_method
end
Мне тоже не нравится. Я использую
def some_method
@some_method_memo ||= some_method_eval
end
private
def some_method_eval
# lot of code
end
Здесь eval
является сокращением для evaluation
. Мне нравится, как это читается, а также упрощает публичный интерфейс.
Я презираю соглашения, которые полагаются на подчеркивания как отличительные знаки: они оба склонны к ошибкам и требуют, чтобы я помнил YAMC (еще одно бессмысленное соглашение). Язык Ada, предназначенный для критически важных приложений, не позволяет вести ведущие, конечные или множественные символы подчеркивания. Хорошая идея.
Я обычно делаю это как в Agis или:
def filesize() @filesize ||=
calculate_filesize
end
BTW:
Я часто использую этот метод memoization:
def filesize() @_memo[:filesize] ||=
calculate_filesize
end
Это позволит вам позже очистить все замеченные переменные одним простым @_memo.clear
. Значение переменной @_memo должно быть инициализировано следующим образом: Hash.new { |h, k| h[k] = Hash.new }
.
Это дает вам много преимуществ использования ActiveSupport:: Memoize и аналогичных метапрограммированных методов, которые могут быть намного медленнее.