2019
Do not Repeat Yourself - при разработке программного обеспечения необходимо устранять ненужное дублирование
Если логика изменится, то нужно исправить только в 1 месте
В каждом из этих случаев следует избегать дублирования
Классы описывают общую логику действий для множества различных объектов: методы класса доступны всем объектам
Вызов метода в Ruby - передача сообщения, состоящего из
Когда объект получает сообщение, то оно передаётся классу данного объекта для обработки
Поиск метода происходит только по имени, из аргументов учитывается только лишь их количество
Механизм наследования позволяет создавать новые версии класса, являющиеся его уточнением или специализацией
class Parent
...end
class Child < Parent
end
<
Child
является подклассом Parent
self
содержит ссылку на объект, на котором вызван методclass Parent
def say_hello
"Hello from #{self}"
puts end
end
Parent.new
parent =
puts parent.say_hello
class Child < Parent
end
Child.new
child = puts child.say_hello
superclass
Object
Object
является BasicObject
, у которого нет суперкласса"Child superclass: #{Child.superclass}"
puts "Parent.superclass: #{Parent.superclass}"
puts "Object.superclass: #{Object.superclass}"
puts "BasicObject.superclass "\
puts "#{BasicObject.superclass.inspect}"
Напомним, что общение между объектами в Ruby напоминает отправку сообщения: в сообщении указывается название метода и его аргументы.
Когда объект получает сообщение, то он начинает поиск метода, которому можно передать данное сообщение:
Первый найденный метод в данной цепочке будет вызван и ему будут переданы все переданные аргументы
super
, который является альтернативным названием родительского методаObject
to_s
, преобразующий текущий объект к строкеclass Person
def initialize(name)
@name = name
end
end
Person.new("Michael")
person = # <Person:0x007fc812839550>
puts person
class Person
def initialize(name)
@name = name
end
def to_s
"Person named #{@name}"
end
end
Person.new("Michael")
person = # Person named Michael puts person
При написании сложных приложений разработчики зачастую сталкиваются с проблемой роста:
В Java
проблема решается обязательным выделением пакетов, а в последней версии и модулей со встроенными версиями.
Модуль - замкнутое именованное пространство
module Trig
PI = 3.141592654
def Trig.sin(x) # Определяем метод модуля
# ..
end
def Trig.cos(x)
# ..
end
end
Trig::PI # Доступ к константе
puts Trig.sin(Trig::PI/4) # Вызов метода puts
::
module Moral
VERY_BAD = 0
BAD = 1
def Moral.sin(badness)
# ...
end
end
Trig.sin(Trig::PI/4)
y = Moral.sin(Moral::VERY_BAD) wrongdoing =
Определение методов модулей похоже на определение методов классов (не методов экземпляров классов). Можно даже использовать синтаксис с ключевым словом self
module ModuleWithBigName
def ModuleWithBigName.greet(name)
"Hello, #{name.upcase}"
puts end
def self.info
'I am working'
end
end
ModuleWithBigName.greet('mariya')
ModuleWithBigName.info puts
Оба метода в примере являются методами одного модуля
К константам, которые можно определить в модуле, относятся также модули и классы
module External
class Internal
...end
end
External::Internal.new object =
Ввиду того, что модули тоже являются константами, то их содержимое можно определять в нескольких файлах, эффективно создавая замкнутые пространства имён
Содержимое файла lib/application/car.rb
module Application
class Car
...end
end
Содержимое файла lib/application/house.rb
'car'
require_relative module Application
class House
def initialize
@car = Car.new
end
end
end
Предположим, что мы разрабатываем библиотеку для создания графического пользовательского интерфейса и мы определили следующие сущности: базовую и двух наследников
Если мы захотим создать новый элемент, который реализует прокручиваемый список элементов, то нам будет необходимо унаследоваться от последних двух классов. Вопросы:
Модули позволяют решить вопросы множественного наследования, реализуя возможность агрегирования: модули можно примешивать к определениям классов
module Debug
def who_am_i? # Экземпляр метода
"#{self.class.name} (id: #{self.object_id})"
end
end
class Test
Debug # Примешивание модуля Debug
include end
Test.new.who_am_i?
include
include
не подключает файл, а только ссылается на описанный ранее модуль. Если примесь определена в отдельном файле, то его необходимо подключитьinclude
не копирует методы из модуля в класс, а только лишь добавляет ссылку в класс
include
подключает только лишь методы экземпляра, для подключения методов модуля в методы класса необходимо использовать метод extend
extend
module Mixin
def self.class_method
'In the class method'
puts end
def instance_method
'In the instance method'
puts end
end
class Test
Mixin
extend end
Test.class_method
Test.new
test = # Не сработает test.instance_method
Множественное наследование (C++)
Один класс-родитель (Java < 8, C#)
В Java 8 интерфейсы могут содержать код, который может выполняться. Это открывает возможности по использованию их в качестве примесей, однако такой практики пока ещё не сложилось.
Язык Ruby включает в себя ряд полезных примесей, которые могут пригодится при создании собственных объектов
Примесь Comparable
опирается на то, что в классе будет реализован метод сравнения <=>
. Данный метод берёт ссылку на другой объект, сравнивает и возвращает
Большинство встроенных типов данных реализуют данный метод.
Используя этот метод примесь добавляет в ваш класс следюущие методы: <
, <=
, ==
, >
, >=
, between
, clamp
.
class SizeMatters
Comparable
include :str
attr def <=>(other)
str.size <=> other.str.sizeend
def initialize(str)
@str = str
end
end
SizeMatters.new("Z")
s1 = SizeMatters.new("YY")
s2 = SizeMatters.new("XXX")
s3 = #=> true
s1 < s2 #=> false s4.between?(s1, s3)
Модуль Enumerable
предоставляет большое число методов для работы с данными, которые можно перечислить.
Классу, использующему модуль Enumerable необходимо реализовать следующие методы:
each
для обхода всех элементов коллекции<=>
После примеси модуля становятся доступными все его методы:
map
find
reduce
sort
Из рассмотренных выше примесей мы можем вынести следующие уроки:
Comparable
таким свойством явился метод <=>
Enumerable
таким свойством явился метод each
Методы, примешанные к классу, могут взаимодействовать не только с методами, но также и с переменными экземпляра
module First
def foo
@abc = 10
end
end
module Second
def bar
@abc = 'abc'
end
end
Не следует разрабатывать модулей, которые оперируют состоянием переменных экземпляра напрямую
Когда у вас появляется возможность использовать примеси, то должен встать вопрос: как происходит поиск методов среди всех связных компонент? Порядок достаточно простой:
Зависит ли порядок поиска методов в примесях от порядка их включения в класс?
Механизм наследования позволяет определить чёткую иерархию свойств объектов. Для любого класса-ребёнка должно выполняться правило: класс-ребёнок является классом-родителем. Т.е. в программе можно заменить класс-родитель на класс-ребёнок и приложение должно продолжить правильно функционировать.
В большинстве случаев реального мира мы имеем дело с предметами, обладающими множеством различных свойств и множеством контрагентов. То есть объект живёт в некоторой среде и комбинирует в себе качества множества различных компонент. Такое поведение обычно сложно представить в виде подмножества какого-то конкретного класса.
Простое правило, которое следует использовать при проектировании классов: «композиция лучше наследования»