Ruby:如何防止通过属性读取器修改数组实例变量

m_x*_*m_x 9 ruby arrays getter encapsulation instance-variables

对不起这个菜鸟问题...让我们说:

class TestMe
 attr_reader :array

 def initialize
   @array = (1..10).to_a
 end
Run Code Online (Sandbox Code Playgroud)

结束

然后可以这样做:

>> a = TestMe.new
=> #<TestMe:0x00000005567228 @x=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]>
>> a.array.map! &:to_s
=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
>> a.array
=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
Run Code Online (Sandbox Code Playgroud)
  • 这明显违背了封装,不是吗?
  • 有没有办法快速保护数组变量不被改变?
  • ...或者每当我的实例变量具有"破坏性"方法时,我是否需要实现深拷贝读取器?

编辑我在某处读到暴露数组实例变量是"坏OO".如果这是真的,为什么?

Ali*_*kau 11

你不能做太多attr_reader,因为attr_reader :array生成以下代码:

def array; @array; end
Run Code Online (Sandbox Code Playgroud)

如果您不想公开数组实例,则可以返回此数组的Enumerator(外部迭代器).Enumerator是一个很好的迭代器抽象,不允许你修改原始数组.

def array; @array.to_enum; end
Run Code Online (Sandbox Code Playgroud)

什么对封装有好处,什么不取决于你的班级所呈现的抽象.通常,这不利于封装以暴露对象的内部状态,包括内部数组.您可能希望公开一些操作@array而不是暴露@array(甚至是它的迭代器)本身的方法.有时这可以暴露数组 - 总是看看你的类提出的抽象.


KL-*_*L-7 5

如何从getter返回原始数组的副本:

class TestMe

  attr_writer :array

  def initialize
    @array = (1..10).to_a
  end

  def array
    @array.dup
  end

end
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您无法直接修改原始数组,但使用属性编写器可以将其替换为新数组(如果需要).