2016-05-23 8 views
0

В Ruby существует разница между следующими двумя четырьмя случаями? Если да, то что лучше?Лучшая практика Ruby для возвращаемого экземпляра varaible, которая не может быть определена

class OptionOne 
    def initialize(arr) 
    @arr = arr 
    end 
    def arr 
    @arr || [] 
    end 
end 

class OptionTwo 
    def initialize(arr) 
    @arr = arr 
    end 
    def arr 
    (defined? @arr) ? @arr : [] 
    end 
end 

Третий вариант, в соответствии с Руководством по Ruby, стиль упомянутого Ильей:

class OptionThree 
    def initialize(arr) 
    @arr = arr 
    end 
    def arr 
    @arr ||= [] 
    end 
end 

И четвертый вариант от ответа от Кита Беннетта:

class OptionFour 
    attr_accessor :arr 
    def initialize(arr = []) 
    @arr = arr 
    end 
end 
+0

Что вы пытаетесь сделать с линиями '@ arr'? –

+1

В этом коде нет способа инициализировать @arr со значением, поэтому @arr всегда будет nil (не определено). Кроме того, в OptionOne # arr вам нужно сказать '@ arr' (обратите внимание на '@'). В противном случае функция вызовет себя до тех пор, пока вы не получите переполнение стека. –

+0

Если это просто пустой массив, с которым вы инициализируете переменную, почему бы не выполнить инициализацию в конструкторе и избежать необходимости в этом? Величина ленивой инициализации (инициализация отсрочки до ее появления) возникает, когда инициализация является дорогостоящей (во времени, в памяти, других ресурсах и т. Д.). –

ответ

5

Примечание: это ответ на исходный вопрос.

Согласно Руби style guide, лучше использовать memoization:

def arr 
    @arr ||= [] 
end 

a ||= b только сокращенную для a || a = b. Подробнее об этом here.

+0

Спасибо за совет (и действительно полезную ссылку). Так будет ли OptionThree в отредактированном вопросе быть тем, кто будет использовать? –

+0

В любом случае 'a || = b' является сокращением для' a || a = b' (то есть 'a' устанавливается только в том случае, если оно ложно). Но '|| =' по-прежнему отличается при использовании методов доступа, хешей, массива ... В любом случае 'a' оценивается только один раз. –

+0

спасибо, @HolgerJust. Я обновил свой ответ, это была очень полезная информация от вас. – Ilya

1

Это зависит от того, что вы хотите сделать с полученным объектом.

Ваш вариант всегда возвращает новый объект, если значение @arr не установлено ни в каком истинном значении. Это гарантирует, что это значение не может повлиять на состояние вашего экземпляра OptionOne. Основное отличие от OptionThree является поведение для этого случая:

option_one = OptionOne.new(nil) 
arr = option_one.arr 
arr[0] = "Hello" 

arr 
# => ["Hello"] 
option_one.arr 
# => [] 

против для OptionThree:

option_three = OptionThree.new(nil) 
arr = option_three.arr 
arr[0] = "Hello" 

arr 
# => ["Hello"] 
option_three.arr 
# => ["Hello"] 

В случае OptionThree, единый объект массива будет сохраняться на объекте опции и таким образом изменяемый любым последующим кодом. В случае OptionOne вы всегда возвращаете новый объект Array.

Обратите внимание, что это различие выполняется только в том случае, если @arr не был инициализирован значением истины (например, массивом). В этом случае оба варианта показывают поведение OptionThree. Часто вы, таким образом, видите OptionThree, чтобы сохранить такое же поведение для всех случаев.

Если вы знаете, что хотите сохранить внутреннее состояние объекта опций во всех случаях, может возникнуть смысл возвращать новый объект каждый раз.

Теперь, чтобы OptionTwo, это не сработает. Поскольку вы всегда устанавливаете @arr в инициализаторе, он всегда будет установлен и, следовательно, будет возвращен. Таким образом, часть else никогда не будет выполнена. Ваш OptionTwo таким образом, может быть сокращен до:

def OptionTwo 
    def initialize(arr) 
    @arr = arr 
    end 

    attr_reader :arr 
end 

Обратите внимание, что при использовании defined? с переменным экземпляром почти всегда плохая идея. В Ruby переменные экземпляра неявно инициализируются во многих случаях, например. при чтении в любом месте (независимо от того, было ли оно ранее задано явное значение). Таким образом, опираясь на поведение defined?, вы должны быть очень осторожны, чтобы не случайно определить его. Часто гораздо удобнее предположить, что он определяется и обрабатывает поведение, основанное на его значении.

Если nil и false не являются допустимыми значениями для @arr, вы можете всегда безопасно инициализировать его в массив с помощью ||=. Только любое из них является допустимым значением, вы должны добавить дополнительную логику для выбора правильной инициализации.

+0

Спасибо за очень проницательный ответ; Теперь я вижу субтитры разных вариантов. –