«Рубиновый путь» (mixins и повторное открытие классов) против внедрения зависимостей - PullRequest
18 голосов
/ 15 августа 2011

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

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

(см. № 6 в http://weblog.jamisbuck.org/2007/7/29/net-ssh-revisited)

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

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

Например, класс Foo здесь связан с классом Logger:

class Foo
  def initialize
    @logger = new_logger
  end

  def new_logger
    Logger.new
  end
end

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

Ответы [ 5 ]

9 голосов
/ 15 августа 2011

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

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

5 голосов
/ 15 августа 2011

Ну, просто потому, что мы можем открыть классы в 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, все будет работать нормально.

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

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

2 голосов
/ 11 сентября 2011

Статья о том, как использовать 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.

2 голосов
/ 15 августа 2011

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

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

1 голос
/ 15 августа 2011

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

...