2016-05-23 22 views
0

在Ruby中,以下两个四种情况是否有区别?如果是这样,哪种更好的做法?Ruby返回实例变量的最佳做法,可能未定义

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总是为零(未定义)。另外,在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?的行为时,必须非常小心,以免意外定义它。通常,假定它被定义并根据其值处理行为更为方便。

如果nilfalse对于@arr不是有效值,则始终可以使用||=将它安全地初始化为数组。只有其中的任何一个是有效值,您必须添加额外的逻辑来选择正确的初始化。

+0

非常有见地的答案,谢谢。我现在看到不同选项的字幕。 –