Search code examples
rubyarraysencapsulationinstance-variablesgetter

Ruby : how to prevent modification of an array instance variable through an attribute reader


sorry for this noob question... let's say we have :

class TestMe
 attr_reader :array

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

end

it is then possible to do :

>> 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"]
  • this clearly goes against encapsulation, doesn'it ?
  • is there any way to quickly protect the array variable from being changed ?
  • ... or do i need to implement a deep-copy reader every time my instance variable has "destructive" methods ?

EDIT i read somewhere it is "bad OO" to expose an array instance variable. If it's true, why ?


Solution

  • You cannot do much with attr_reader, because attr_reader :array generates the following code:

    def array; @array; end
    

    If you don't want to expose array instance, you can return Enumerator of this array (external iterator). Enumerator is a good iterator abstraction and does not allow you to modify original array.

    def array; @array.to_enum; end
    

    What good for encapsulation and what not depends on the abstraction your class presents. Generally this is not good for encapsulation to expose internal state of an object, including internal array. You may want to expose some methods that operate on the @array instead of exposing @array (or even its iterator) itself. Sometimes this is fine to expose array - always look at the abstraction your class presents.