Ruby: вложение классов в классы так же, как вложение классов в модули?

#ruby #oop #nested-class

#ruby #ооп #вложенный класс

Вопрос:

В Ruby можно объявлять классы типа

 class A
    class B
    end
end
 

а затем создайте экземпляр внутреннего класса, например A::B.new .

Имеет ли B какое-то особое отношение к A (как в случае с Java) или это A просто его пространство имен? Другими словами, является ли вложение класса в класс таким же, как вложение его в модуль?

Ответ №1:

Это один из тех случаев, когда вам действительно нужно просто запустить irb и попробовать.

Да, класс может быть определен внутри другого класса. Поскольку суперклассом класса является Module (т. Е. Класс расширяет модуль), они могут делать почти все, что может модуль. Заметным исключением является то, что вы не можете include (смешивать) класс.

Тем не менее, на самом деле очень мало причин использовать этот шаблон проектирования. Более вероятно, что A и B можно считать братьями и сестрами. Нет необходимости произвольно использовать пространство имен, пока вы не посинеете. Если глубина более 3 уровней, остановитесь и подумайте о рефакторинге.

Лучшим шаблоном проектирования для этого примера может быть:

 module Alphabet
  class A; end
  class B; end
end
 

Для чего-то более сложного это продолжает хорошо работать с использованием миксинов:

 module Animals
  class Dog
    include Walking
  end

  module Walking
    # some methods pertaining to the ability to walk
  end
end
 

Комментарии:

1. Ваш ответ хорошо предвосхищает проблемы проектирования, о которых, вероятно, придется подумать любому, кто интересуется этим вопросом, но afaict не отвечает на вопрос напрямую. Я намеренно удалил свой вопрос из предметной области, чтобы сделать его чисто техническим вопросом «да / нет», но я думаю, что это все еще хороший ответ в этой теме.

2. Я прямо отвечаю на вопрос во втором предложении и объясняю, почему. Я отредактировал свой вопрос, чтобы добавить немного больше информации о том, почему эту функцию лучше оставить в покое.

3. Я уже знал, что класс может быть определен внутри другого класса. Вопрос заключался в том, ведет ли это себя точно так же, как класс, определенный в модуле, или внутренний класс имеет какие-то особые отношения с внешним классом, которых нет у класса в модуле. Может быть, вы ошибочно приняли первое предложение вопроса за вопрос 🙂 В любом случае я понимаю и согласен с вашим советом.

4. Ого, я перенес «можно» в ваше первое предложение и прочитал «можно ли объявлять классы, подобные». Думаю, я явно не указывал, что связь между A и B такая же, как если бы A был модулем, но это так, да. У них действительно есть связь, но она не отличается от класса в модуле, просто для ясности.

Ответ №2:

Да, это, по сути, то же самое, что вложенность в module. Вложенный класс не имеет никакого особого отношения к классу, в который он вложен.

Ответ №3:

Да and…no . Модуль не может быть создан, тогда как класс может, поэтому существует разница во вложении класса в модуль по сравнению с использованием класса, вложенного в класс.

Например: вы не можете создать экземпляр объекта Ford здесь (например, Cobra, F-150, Mustang и т. Д.), Потому что вы не можете создать экземпляр модуля,.

          `module Ford
            class Engine
              ...
            end
          end`
 

Но у вас может быть экземпляр класса Ford, а также экземпляры двигателей Ford с использованием вложенных классов, например:

        `class Ford
           class Engine
             @@actions = ['list', 'find', 'add', 'quit']
             def self.actions; @@actions; end
           end

           def self.truck_exists?
             #Some more stuff here
           end
        end` 
 

Ответ №4:

Классы (и модули) имеют разное использование:

  • Пространство имен: когда вы определяете класс (или модуль) внутри другого класса (или модуля), вы должны использовать другую нотацию вне определяющего класса, чтобы получить доступ к внутреннему классу или модулю.
  • Шаблон для экземпляров: там модули и классы ведут себя совершенно (ну, в основном) по-разному.
  • Наследование: вы можете расширить класс, чтобы наследовать все его атрибуты и методы, что также работает для модулей. Или вы можете включить модуль (не: класс), чтобы добавить его методы (и атрибуты) в вашу область. Ruby использует здесь трюки, смешивая модуль в иерархии классов.

Итак, в контексте использования пространств имен они одинаковы. Однако я бы все время использовал модули (только), но это скорее вопрос стиля. Лучшая книга на эту тему, на мой взгляд, «Метапрограммирование Ruby» Паоло Перротты.

Комментарии:

1. Поведение модулей и классов не отличается, за исключением того, что класс может быть создан. Метапрограммирование Ruby — хорошая книга, но вы, возможно, захотите прочитать ее еще раз.