Подтвердить что ты не робот

"Рубиновый путь" (миксины и повторное открытие класса) против инъекции зависимостей

При изучении микшинов и инъекций зависимости я часто слышу фразу "путь Ruby". Часто разработчики говорят что-то вроде

Ruby позволяет повторно открывать классы и переопределять методы, что позволяет легко "вводят" новые ссылки в ваш код во время тестирования.

(см. # 6 на http://weblog.jamisbuck.org/2007/7/29/net-ssh-revisited)

Но тестирование не является моей главной заботой; моя забота - повторное использование класса. Я хочу, чтобы классы я мог повторно использовать в нескольких приложениях Rails корпоративного масштаба.

Итак, что случилось с классами REUSING? Использование mixins и классов повторного открытия, похоже, не дает возможности писать классы таким образом, чтобы они были отделены от конкретных приложений без дополнительной работы. Но, возможно, я ошибаюсь. Если да, может ли кто-нибудь предоставить ссылку на статью, содержащую образец кода, в котором четко объясняется, как это сделать правильно, используя mixins и повторное открытие классов?

В качестве примера класс Foo здесь связан с классом Logger:

class Foo
  def initialize
    @logger = new_logger
  end

  def new_logger
    Logger.new
  end
end

Да, я могу повторно открыть Foo и переопределить new_logger, но я просто не могу поверить, что это считается реалистичным стандартным подходом к написанию многоразовых классов, используемых несколькими приложениями Rails.

4b9b3361

Ответ 1

На самом деле, когда я пришел из мира Java в мир рубинов, первое, что меня интересовало, - это то, как они управляют зависимостями. В то время я использовал Google Guice во всех проектах java, и я был действительно вдохновлен его умным дизайном и простотой использования. Первое, что я сделал в ruby, - это мой собственный контейнер DI, который имел примерно тот же набор функций, что и Google Guice - (он по-прежнему здесь, на github, но он очень устарел).

Но теперь, после двух лет работы с Rails/Ruby, я думаю, что DI здесь не нужен. Хорошая статья о DI в ruby ​​http://weblog.jamisbuck.org/2008/11/9/legos-play-doh-and-programming - это фактически статья о том, почему DI не нужен автору одного из первых контейнеров DI для рубина. Это определенно стоит прочитать.

Ответ 2

Хорошо, только потому, что мы можем открывать классы в Ruby, это не значит, что мы всегда должны это делать, вы можете подумать о повторном открытии классов как о методе последней инстанции. У вас есть библиотека, которая делает все, что вам нужно, кроме одного метода, вместо того, чтобы разворачивать всю библиотеку, исправляя ее и используя вашу вилку, вы можете просто снова открыть класс, переопределить метод и снова заняться бизнесом. Это не то, что вы делаете волей-неволей, но иметь возможность сделать это чрезвычайно полезно.

Сказав все это, в Ruby у нас есть концепция, которая почти всегда может быть хорошей заменой инъекции зависимостей - утиной типизацией. Так как нет проверки типа, вы можете передать какой-либо объект в функцию и до тех пор, пока объект имеет методы, ожидаемые функцией, все будет работать нормально.

Давайте посмотрим на ваш пример - на самом деле это не класс, способствующий внедрению зависимостей, вы бы не записывали его так, как это на Java, например, если вы хотели бы добавить некоторые зависимости. Вы можете вводить только через конструктор или через геттеры и сеттеры. Поэтому перепишите этот класс таким образом:

class Foo
  def initialize(logger)
    @logger = logger
  end
end

Намного лучше теперь мы можем ввести/передать в регистратор в наш класс Foo. Добавьте метод, который будет использовать этот регистратор, чтобы продемонстрировать:

class Foo
  def initialize(logger)
    @logger = logger
  end

  def do_stuff
    @logger.info("Stuff")
  end
end

В java, если вы хотите создать объекты Foo с разными типами регистраторов, все эти регистраторы должны будут реализовать один и тот же интерфейс в буквальном смысле (например, public class logger implements Loggable) или, по крайней мере, быть дочерними классами. Но в Ruby до тех пор, пока объект имеет метод info, который принимает строку, вы можете передать его в конструктор, и Ruby продолжает весело кататься. Продемонстрируем:

class Logger
  def info(some_info)
  end
end

class Widget
  def info(some_widget_info)
  end
end

class Lolcat
  def info(lol_string)
  end
end

Foo.new(Logger.new).do_stuff
Foo.new(Widget.new).do_stuff
Foo.new(Lolcat.new).do_stuff

Со всеми тремя примерами класса Foo, вызывающим метод do_stuff, все будет работать нормально.

Как вы можете видеть из этого примера, придерживаться принципов дизайна OO по-прежнему важно, но Ruby несколько менее ограничивает то, что он примет, если правильные методы там, все будет в порядке.

В зависимости от того, как вы смотрите на это, утка печатает либо делает инъекцию зависимостей совершенно неактуальной, либо делает ее более мощной, чем когда-либо.

Ответ 3

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

Тем не менее, я знаю, что обычно предпочтительнее прибегать к конструктивному решению с языковыми конструкциями, и, насколько мне известно (я предупреждаю вас, далек от завершения по этому вопросу), DI не является в настоящее время основным рубизмом. Немного о Googling нашел мне некоторые статьи по этому вопросу, хотя, заставив меня поверить, что есть библиотеки, которые можно найти, чтобы добавить DI в Ruby, если вы этого желаете (хотя они, похоже, получают критику со стороны многих рубистов). В информационные статьи включены эти два и этот вопрос SO. Надеюсь, что это поможет.

Ответ 4

Статья о том, как использовать IoC в Ruby Вы недооцениваете силу IoC

С рабочими образцами.

ОБНОВЛЕНИЕ

Ссылка уже мертва, здесь источник https://github.com/alexeypetrushin/rubylang/blob/master/draft/you-underestimate-the-power-of-ioc.md

Я использовал IoC как часть моей веб-инфраструктуры, я как-то заново создал Ruby on Rails, и это сработало, но оно не даст значительного преимущества перед RoR, имело схожие характеристики. Итак, это стало обузой, и я отказался от нее, некоторые детали http://petrush.in/blog/2011/rad-web-framework.

Ответ 5

Я полностью согласен. Динамические языки не заменяют инъекции зависимостей. И ничто не мешает вам писать один для динамического языка. Здесь инфраструктура инъекции зависимостей для Smalltalk: http://www.squeaksource.com/Seuss.html