使类实例变量从外部世界不可写

时间:2015-04-22 17:48:49

标签: ruby class-instance-variables

class Event

    @event_list = {}

    attr_reader :name, :value

    def initialize(name, value)
      @name  = name
      @value = value
    end

    def to_s
      "#{@value}"
    end

    class << self

      def event_list
          @event_list
      end

      def event_list=(value); end

      def register_event(name, value)
          @event_list[name] = Event.new(name, value)
      end

      def registered_events
          event_list
      end
    end
end

在上面的代码片段中,我可以使用Event.event_list访问@event_list,有趣的是我能够从外部修改此变量

Event.event_list[:name] = "hello"
Event.event_list  # => { :name => 'hello' }

我怎样才能避免这种情况?我不想从外面修改@event_list。

4 个答案:

答案 0 :(得分:1)

据我所知,您无法阻止外部代码修改Ruby中的实例变量。即使您不使用attr_readerattr_writer,仍然可以使用Object#instance_variable_set访问它。 Ruby没有私有变量(或常量),只有礼貌要求你不要修改的变量。

如果您没有定义event_list=,那么这被视为@event_list是私有变量的指示。这是解决您问题的方法。

然后是可变对象的问题。因为Ruby中几乎所有对象都是可变的,所以通常只要你能获得对象的引用,就可以改变它。

这可以通过Object#freeze解决,它可以阻止对象被修改。不幸的是,这意味着即使你可以修改它。

Ruby对于锁定事物并不是很好。这种开放性是您可能需要学习使用的语言的核心部分。

答案 1 :(得分:1)

正如其他人所说,只需将方法设为私有:

class Event
    @event_list = {a: 'dog'}
    class << self
      def pub_event_list
        @event_list
      end  
      def pub_event_list=(other)
        @event_list=other
      end  
      private
      def event_list
        @event_list
      end
      def event_list=(value)
        @event_list = value
      end
    end
end

Event.event_list
  #=> NoMethodError: private method `event_list' called for Event:Class

Event.pub_event_list
  #=> {:a=>"dog"} 

Event.event_list= {b: 'cat'}
  #=> #NoMethodError: private method `event_list=' called for Event:Class

Event.pub_event_list= {b: 'cat'}
  #=> {:b=>"cat"} 

答案 2 :(得分:0)

您在单身人士上定义def event_list,提供对变量的访问权限。将该方法设为私有

编辑:

当您声明@event_list时,它在您的单例方法中具有范围。

class Event
  @event_list = {}

  class << self
    def event_list
      # scoped from above
      @event_list
    end
  end
end

Event.event_list #=> {}

要删除访问权限,只需将方法设为私有:

class Event
  @event_list = {}

  class << self
    private

    def event_list
      # scoped from above
      @event_list
    end
  end
end

Event.event_list #=> NoMethodError: private method `event_list' called for Event:Class

答案 3 :(得分:0)

以下是我现在所做的事情,我不知道这是不是一个好的解决方案

class Event

  @event_list = {}

  attr_reader :name, :value

  def initialize(name, value)
    @name  = name
    @value = value
  end

  def to_s
    "#{@value}"
  end

  class << self
    def register_event(name, value)
      @event_list = @event_list.merge(name => Event.new(name, value))
    end

    def registered_events
      @event_list.freeze
    end
  end
end

现在我可以访问@event_list而不让其他人修改。