Pull to refresh

Comments 15

Отличная статья, не знал про hook методы.
Возможность переопределить методы класса (даже с сохранением старой реализации; a-la virtual-override у C#) — одна из первых фич в Руби, о которой я узнал и полюбил.
Это называется «полиморфизмом», зачем выдумывать дополнительную терминологию?
А я то все думал, что совсем отстал от жизни — про паттерны знать ничего не знаю, а оно вот в чем дело.
Как всегда, круче всех тот, кто придумает новый модный термин для старых всем известных подходов или технологий.
Я думал это просто переопределение свойств/методов класса, а оно оказывается «Hook методы». Эвона как… :)
Прошу автора не обижаться. Мое бурчание скорее адресовано общемировым тенденциям в IT
А скелетный класс-то чего стоит)
Мне кажется паттерны проектирования как раз таки и придуманы для того чтобы показать какие есть шаблонные подходы по применению наследования, инкапсуляции и полиморфизма.
То есть можно ожидать переводов о паттернах «общий родитель» про наследование и «скрытая реализация» про инкапсуляцию? =)
Как раз сейчас читаю Design Patterns in Ruby, там и про шаблоны и про все! Советую!
Автор книги в теме про паттерны, но не очень хорошо оперирует Ruby.

def Wall length, width, material
  Module.new do
    define_method(:length) { length }
    define_method(:width) { width }
    define_method(:material) { material }
  end
end

class BaseWall
  def dimensions
    "I am #{length}ft. long and #{width}ft. wide!"
  end

  def made_from
    "I am made from #{material}!"
  end
end

class BrickWall < BaseWall
  include Wall(30, 20, 'brick')
end

class ConcreteWall < BaseWall
  include Wall(30, 20, 'concrete')
end

class WoodWall < BaseWall
  include Wall(10, 20, 'wood')
end

Не очень понятно, не могли бы вы пояснить свою реплику и прокомментировать этот кусок кода?
С удовольствием.
В верхнем блоке кода мы пользуемся недоступным в большинстве других языков метапрограммированием, являющимся одним из ключевых преимуществ Ruby. Мы делаем шаблон для Module, позволяя создавать его на лету. Wall, как следует из предшествующего перед ним def — это метод, который определяется в глобальном контексте, возвращающий объект типа Module с тремя заданными параметрами. Обычно методам принято давать имена со сточной буквы, но в данном случае, для красоты, Wall идёт с прописной. Для пущей красоты, кстати, можно этот метод положить в BaseWall, сделав его статическим (def self.Wall ...). Следующие два блока кода эквивалентны:
  WoodWallModule = Wall(10, 20, 'wood')

module WoodWallModule
  def length
    10
  end

  def width
    20
  end

  def material
    'wood'
  end
end


Включая (include) «примесь», то есть module в класс, мы можем использовать все заданные в этом module методы, как если бы они были заданы локально.

Остальное вроде бы как в примере из той книги, наследование от BaseWall, определяющего общие для всех стен методы (dimensions, made_from), использующие частные (length, width, material).

Из недостатков вижу то, что метод Wall определён в глобальном контексте, но это легко исправляется переносом его в BaseWall статическим методом.
Второй недостаток — в отличие от примера из книги методы length, width, material являются доступными, public для объектов стен. Хотя и не указано обратное, но будем точно следовать поведению кода из книги. Это слегка подковыристо, но нужно добавить в определение Module.new статический метод included, который вызывается, когда этот module включают (примешивают) в какой-то класс, и установить видимость тех методов, которые мы хотим скрыть в private:
  def self.included clazz
    private :length, :width, :material
  end


Итого с этими двумя модификациями:
class BaseWall
  def self.Wall length, width, material
    Module.new do
      define_method(:length) { length }
      define_method(:width) { width }
      define_method(:material) { material }

      def self.included clazz
        private :length, :width, :material
      end
    end
  end

  def dimensions
    "I am #{length}ft. long and #{width}ft. wide!"
  end

  def made_from
    "I am made from #{material}!"
  end
end

class BrickWall < BaseWall
  include Wall(30, 20, 'brick')
end

class ConcreteWall < BaseWall
  include Wall(30, 20, 'concrete')
end

class WoodWall < BaseWall
  include Wall(10, 20, 'wood')
end
Если у вас вызывает трудности чтение этого кода, вам нужно начать читать why_'s Poignant Guide to Ruby, а не Design Patterns. Ну или как минимум Objects on Rails от Avdi Grimm.

Моя реплика же судит ужасный код автора книги, который написан на Java, но с синтаксисом Ruby.
То, что в Ruby можно так делать, не значит, что так делать нужно или хорошо в данном случае. Оригинальную статью писал не автор книги. Собственно, в книге пример чуть более комплексный. Для него такой способ не подойдёт.
Оценку можно произвести, если определиться с набором аспектов, по которой она идёт. Здесь выбирал читаемость, краткость и возможность повторного использования. Если вы видите какие-то недостатки, или они могут быть выявлены с более комплексном примере — можно посмотреть и на него. Я так понимаю, что это тема как раз для следующего топика топикстартера.
Sign up to leave a comment.

Articles