Instance variables, preventing changes once its va

2019-06-13 23:03发布

问题:

There is a class Section (as part of MyModule mixin).

Its instance variable @mark is initialized to nil, and can be changed by an instance method change_mark when its current value is nil, but not when it's been set to something else. I'm trying to build some protections to prevent the instance variable from being set in such case.

Here's what I have:

module MyModule
  class Section
    attr_accessor :id, :mark

    def initialize(id, mark)
      @id = id
      @mark = mark
    end
    def change_mark(mark)
      if @mark != nil?
        message = "Section ##{@id} is already marked. Choose another."
        raise RuntimeError.new(message)
      else
        @mark = mark
      end
    end
  end
end

Is raising an error the simplest way to prevent changes to a previously-set instance variable? If not, what can I do?

回答1:

If you want to be that super-strict about not letting users reset the variable, then yes, raise/fail is a good way to go. One note, though: use mark= accessor, so that the users won't have to remember using restrictive change_mark instead of all-allowing default writer mark=.

module MyModule
  class Section
    attr_accessor :id, :mark

    def initialize(id, mark)
      self.id = id
      self.mark = mark
    end

    # overwrite generated setter from attr_accessor
    def mark=(mark)
      if !mark.nil?
        message = "Section ##{id} is already marked. Choose another."
        fail RuntimeError.new(message)
      else
        @mark = mark
      end
    end
  end
end

Or you could just ignore the new value.

def mark=(mark)
  @mark = mark if @mark.nil?
end

This way you won't know which line attempted to reset the value and when. But it's quieter. The choice is yours. :)



回答2:

I would suggest changing the line:

attr_accessor :id, :mark

To two lines like:

attr_accessor :id
attr_reader :mark

So your mark can be changed only via change_mark method, so proper exception is thrown.

With attr_reader :mark there is only "getter" method created for your attribute, so you don't have to wary about any other way of setting your attribute value.

Good luck!