Напомним, что общение между объектами в Ruby напоминает отправку сообщения: в сообщении указывается название метода и его аргументы.
Когда объект получает сообщение, то он начинает поиск метода, которому можно передать данное сообщение:
Если метод есть у класса объекта, то вызывать его
Если метод есть у суперкласса, то вызвать его
Если метод есть у суперкласса суперкласса…
Первый найденный метод в данной цепочке будет вызван и ему будут переданы все переданные аргументы
Перекрытие методов в подклассах
Вы можете в подклассе создать новый метод с именем метода из суперкласса, тогда он будет вызван раньше метода из суперкласса и перекроет последний
Если семантика метода будет сохранена, тогда вызывающий класс не отличит оригинальный метод от подмены
Количество аргументов и их назначение
Возвращаемое значение и использование блока
Для вызова метода из суперкласса используйте специальный метод super, который является альтернативным названием родительского метода
Метод to_s класса Object
Метод преобразует объект к строке, используется стандартными методами вывода на потоки
Рекомендуется реализовать свой метод to_s, преобразующий текущий объект к строке
classPersondef initialize(name)@name = nameendendperson = Person.new("Michael")puts person # <Person:0x007fc812839550>classPersondef initialize(name)@name = nameenddef to_s"Person named #{@name}"endendperson = Person.new("Michael")puts person # Person named Michael
Проблема роста приложений
При написании сложных приложений разработчики зачастую сталкиваются с проблемой роста:
Необходимо придумывать уникальные имена классов, не используемые в других частях приложения и библиотеках
Необходимо логически объединять методы, не принадлежащие ни к одной реальной сущностия
Необходимо логически объединять классы, предназначенные для решения одной большой задаче
В Java проблема решается обязательным выделением пакетов, а в последней версии и модулей со встроенными версиями.
Подходы к решению проблемы
Правильное распределение элементов по файлам
Использование классов для описания сущностей
Объединение классов и функций в модули
Модули в языке Ruby
Модуль - замкнутое именованное пространство
moduleTrigPI = 3.141592654defTrig.sin(x) # Определяем метод модуля# ..enddefTrig.cos(x)# ..endendputs Trig::PI# Доступ к константеputs Trig.sin(Trig::PI/4) # Вызов метода
Для доступа к константам, определённым внутри метода используются два двоеточия ::
Определение методов модулей похоже на определение методов классов (не методов экземпляров классов). Можно даже использовать синтаксис с ключевым словом self
moduleModuleWithBigNamedefModuleWithBigName.greet(name) puts "Hello, #{name.upcase}"enddefself.info'I am working'endendModuleWithBigName.greet('mariya')puts ModuleWithBigName.info
Оба метода в примере являются методами одного модуля
Определение констант в модуле
К константам, которые можно определить в модуле, относятся также модули и классы
Ввиду того, что модули тоже являются константами, то их содержимое можно определять в нескольких файлах, эффективно создавая замкнутые пространства имён
Предположим, что мы разрабатываем библиотеку для создания графического пользовательского интерфейса и мы определили следующие сущности: базовую и двух наследников
Базовый графический элемент, определяющий размеры
Наследник, позволяющий размещать элементы в колонку
Наследник, позволяющий прокручивать своё содержимое
Если мы захотим создать новый элемент, который реализует прокручиваемый список элементов, то нам будет необходимо унаследоваться от последних двух классов. Вопросы:
Сколько раз будут создаваться и настраиваться данные базового класса?
Как будут вести себя переменные, которые используются одновременно в двух классах-наследниках?
Что произойдёт с методами, имеющими одинаковыме названия, но разную реализацию?
Mixin (Примеси, Миксины, Агрегация)
Модули позволяют решить вопросы множественного наследования, реализуя возможность агрегирования: модули можно примешивать к определениям классов
Данные методы становятся методами экземпляра класса
Модуль примешивается с помощью метода include
Особенности примеси модулей
include не подключает файл, а только ссылается на описанный ранее модуль. Если примесь определена в отдельном файле, то его необходимо подключить
include не копирует методы из модуля в класс, а только лишь добавляет ссылку в класс
Если несколько классов примешивают в себя один модуль, тогда они все ссылаются на один и тот же модуль
Если вы изменяете такой модуль, то «автоматически» меняются и все классы, что примешивают данный модуль
include подключает только лишь методы экземпляра, для подключения методов модуля в методы класса необходимо использовать метод extend
Пример использования extend
moduleMixindefself.class_method puts 'In the class method'enddef instance_method puts 'In the instance method'endendclassTest extend MixinendTest.class_methodtest = Test.newtest.instance_method # Не сработает
Другие схемы наследования
Множественное наследование (C++)
У класса может быть множество родительских классов
Каждый из родительских классов может полноценно реализовывать свою логику
Один класс-родитель (Java < 8, C#)
У класса может быть только один родительский класс
Класс может реализовывать множество интерфейсов
В Java 8 интерфейсы могут содержать код, который может выполняться. Это открывает возможности по использованию их в качестве примесей, однако такой практики пока ещё не сложилось.
Использование примеси Comparable
Язык Ruby включает в себя ряд полезных примесей, которые могут пригодится при создании собственных объектов
Примесь Comparable опирается на то, что в классе будет реализован метод сравнения <=>. Данный метод берёт ссылку на другой объект, сравнивает и возвращает
Положительное число, если текущий объект «больше»
Ноль, если текущий объект «равен»
Отрицательное число, если текущий объект «меньше»
Большинство встроенных типов данных реализуют данный метод.
Используя этот метод примесь добавляет в ваш класс следюущие методы: <, <=, ==, >, >=, between, clamp.
Не следует разрабатывать модулей, которые оперируют состоянием переменных экземпляра напрямую
Какой метод будет вызван?
Когда у вас появляется возможность использовать примеси, то должен встать вопрос: как происходит поиск методов среди всех связных компонент? Порядок достаточно простой:
Методы класса, к которому принадлежит объект
Методы примешанных модулей
Методы суперкласса
Методы примешанных в суперкласс модулей
…
Вопрос для самоизучения
Зависит ли порядок поиска методов в примесях от порядка их включения в класс?
Использование примесей и наследования
Механизм наследования позволяет определить чёткую иерархию свойств объектов. Для любого класса-ребёнка должно выполняться правило: класс-ребёнок является классом-родителем. Т.е. в программе можно заменить класс-родитель на класс-ребёнок и приложение должно продолжить правильно функционировать.
В большинстве случаев реального мира мы имеем дело с предметами, обладающими множеством различных свойств и множеством контрагентов. То есть объект живёт в некоторой среде и комбинирует в себе качества множества различных компонент. Такое поведение обычно сложно представить в виде подмножества какого-то конкретного класса.
Простое правило, которое следует использовать при проектировании классов: «композиция лучше наследования»