Menu
Coddy logo textTech

Расширение объектов

Часть раздела Объектно-ориентированное программирование путешествия по Ruby на Coddy. Урок 28 из 57.

extend добавляет методы модуля одному объекту, а не всем экземплярам. Расширение класса добавляет их к объекту класса, поэтому они становятся методами класса:

module Describable
  def describe
    "#{name} has #{instance_methods(false).size} methods"
  end
end

class Dog
  extend Describable

  def bark
    "Woof"
  end

  def sit
    "sits"
  end
end

puts Dog.describe

Вывод:

Dog has 2 methods

Модуль, используемый с extend, может хранить состояние в переменных экземпляра класса. Каждый класс, который его расширяет, получает собственную копию в виде переменных экземпляра класса:

module Counted
  def created
    @created ||= 0
  end

  def record
    @created = created + 1
  end
end

class Order
  extend Counted
end

class Invoice
  extend Counted
end

Order.record
Order.record
Invoice.record
puts "#{Order.created} #{Invoice.created}"

Вывод:

2 1

extend также работает с одним объектом. Только этот объект получает методы; другие экземпляры его класса остаются без изменений, а is_a? имеет значение true только для расширенного объекта:

module Shiny
  def polish
    "#{self} is shining"
  end
end

a = "car"
b = "bike"
a.extend(Shiny)
puts a.polish
puts a.is_a?(Shiny)
puts b.respond_to?(:polish)

Результат:

car is shining
true
false

Эти два ключевых слова помещают методы одного и того же модуля в разные места. include делает их методами экземпляра класса, а extend — методами объекта, который расширяется:

class Order
  include Printable    # order.print_me       (каждый экземпляр)
  extend Counted       # Order.record         (сам класс)
end

item.extend(Shiny)     # item.polish          (только этот объект)
challenge icon

Задание

Легко

Дополните модуль Registry, который классы используют с помощью extend. register(name) добавляет имя в собственный список класса, хранящийся в @entries (создаваемом при первом использовании), и возвращает Plugin registered x; если имя уже есть в списке, возвращается Plugin already has x. entries возвращает список, а count — его размер. Plugin и Theme оба расширяют Registry и хранят отдельные списки.

Предоставленный код считывает строки plugin name или theme name, вызывает register у соответствующего класса и выводит ответ. В конце он выводит plugins: a, b (2) и themes: ... либо none (0) для пустого списка.

Ваш код должен находиться в файлах registry.rb, plugin.rb и theme.rb. В main.rb находится предоставленный код ввода/вывода, и его нельзя изменять.

Попробуйте сами

require_relative 'registry'
require_relative 'plugin'
require_relative 'theme'

# Предоставленный код ввода/вывода: оставьте его как есть
input = []
while (line = gets)
  input << line.chomp
end
input.each do |line|
  kind, name = line.split
  target = kind == "plugin" ? Plugin : Theme
  puts target.register(name)
end
[["plugins", Plugin], ["themes", Theme]].each do |label, klass|
  list = klass.entries.empty? ? "none" : klass.entries.join(", ")
  puts "#{label}: #{list} (#{klass.count})"
end
quiz iconПроверьте себя

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

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Ruby