Как реализовать закрытый внутренний класс в Ruby

Исходя из Java, я пытаюсь реализовать LinkedList в Ruby. Обычный способ реализовать это в Java - это класс LinkedList и закрытый внутренний класс Node с каждым объектом LinkedList в качестве объекта Node.

class LinkedList
  private
  class Node
    attr_accessor :val, :next
  end
end

Я не хочу подвергать класс Node внешнему миру. Однако с этой настройкой в ​​Ruby я могу получить доступ к частному объекту класса Node вне класса LinkedList, используя это -

node = LinkedList::Node.new

Я знаю, что в Ruby 1.9 мы можем использовать метод private_constant для обозначения Node как частной константы. Но мне интересно, если это правильный путь для достижения этой цели? Кроме того, почему я могу создавать объекты Node вне класса LinkedList, даже если он объявлен как закрытый?

2 ответа

Решение

Почему я могу создавать объекты Node вне класса LinkedList, даже если он объявлен как закрытый?

Потому что в рубиновых константах игнорируются "обычные" модификаторы видимости. Они всегда общедоступны, независимо от того, в каком разделе они находятся. Чтобы сделать их приватными, используйте private_constant, Назовите этот не элегантный дизайн или что-то еще, но это так.

Кроме того, имейте в виду, что даже с private_constantПриватность значит очень мало. По сути, единственное, что он делает, это скрывает константу от списков (LinkedList.constants) и прямое разрешение (LinkedList::Node). Если кто-то знает имя, они смогут получить к нему доступ.

class LinkedList
  class Node
    attr_accessor :val, :next
  end

  private_constant :Node
end

LinkedList.const_get('Node') # => LinkedList::Node

Я знаю, что ответа Серхио более чем достаточно, но я просто отвечаю на вопрос:

Как реализовать закрытый внутренний класс в Ruby

Вы также можете пойти с:

class LinkedList
  class << self
    class Node
    end

    def some_class_method
      puts Node.name
    end
  end
end

LinkedList.some_class_method        # accessible inside class
#=> #<Class:0x007fe1e8b4f718>::Node
LinkedList::Node                    # inaccessible from outside
#=> NameError: uninitialized constant LinkedList::Node
LinkedList.const_get('Node')        # still inaccessible
#=> NameError: uninitialized constant LinkedList::Node

Конечно, вы сможете получить доступ Node с

LinkedList.singleton_class::Node
#=> #<Class:0x007fe1e8b4f718>::Node

И это также доступно в LinkedListСинглтон константы класса:

LinkedList.singleton_class.constants
#=> [:Node, :DelegationError, :RUBY_RESERVED_WORDS, :Concerning]

Я обычно использую private_constant, но это еще один способ иметь закрытый класс.

Другие вопросы по тегам