Рубиновая абстракция - PullRequest
       33

Рубиновая абстракция

1 голос
/ 22 декабря 2009

Я новичок в Ruby, в основном из C # и ActionScript 3 (среди других языков). Мне любопытно абстрагировать функциональность. В частности, упаковка и абстракция библиотек Ruby для FTP и SFTP.

Я искал вокруг и наткнулся на камень под названием Backup . Он действительно привлек мое внимание, потому что он поддерживает резервное копирование через S3, SCP, SFTP и FTP. Поэтому я подумал: «Ух ты, вот прекрасный пример!» Я начал просматривать исходный код, но потом наткнулся на код вроде:

case backup.procedure.storage_name.to_sym
  when :s3    then records = Backup::Record::S3.all   :conditions => {:trigger => trigger}
  when :scp   then records = Backup::Record::SCP.all  :conditions => {:trigger => trigger}
  when :ftp   then records = Backup::Record::FTP.all  :conditions => {:trigger => trigger}
  when :sftp  then records = Backup::Record::SFTP.all :conditions => {:trigger => trigger}
end

Просмотр полного источника на GitHub

Это завалено случаями / когда заявления! Если бы я атаковал это в C #, я написал бы интерфейс протокола (или абстрактный класс) и позволил бы FTP и SFTP реализовать это. Тогда мой клиентский класс просто передавал бы экземпляр протокола, не заботясь о реализации. Нулевой переключатель / чехлы.

Буду признателен за небольшую рекомендацию о лучших практиках в этой ситуации при программировании на Ruby.

Ответы [ 5 ]

6 голосов
/ 22 декабря 2009

И вы могли бы сделать это в Ruby тоже

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

И когда я говорю «не нужно», я просто имею в виду, что шаблоны проектирования, на которые вы ссылаетесь, могут быть непосредственно реализованы в Ruby. Поскольку никакие ограничения вызовов не применяются во время компиляции, любой шаблон проектирования, который зависит от интерфейсов или любой разновидности полиморфизма, непосредственно доступен в Ruby.

Да, не похоже, чтобы этот пакет в полной мере использовал возможные абстракции, но, возможно, (а) это не имеет значения, пока он работает. В конце концов, вам не нужно было вводить его, или (b) есть некоторая неочевидная выгода для простого используемого композиционного шаблона.

2 голосов
/ 22 декабря 2009

Есть несколько способов сделать это элегантно, я думаю. Во-первых, использовать send как TK, предложенный выше. Другой - использовать method_missing, метод, который Ruby вызывает, когда не может найти существующий метод.

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

Извиняюсь за то, что не дал вам фрагмент кода, но прочитал его и посмотрел, поможет ли он.

1 голос
/ 23 декабря 2009

В большинстве случаев выражения в языке OO являются признаком того, что вы неправильно используете полиморфизм. В этом случае я бы сделал что-то вроде:

backup.procedure.storage_class.all :conditions => {:trigger => trigger}

Где storage_class возвращает соответствующий класс. (На самом деле, я бы предпочел сделать storage_class свойством самой резервной копии, но я не знаю, практично ли это при разработке этой библиотеки.)

0 голосов
/ 22 декабря 2009

Возможно, есть лучший способ, но это мой выстрел.

ALLOWD_OPTIONS = [:s3, :scp, :ftp ,:sftp].freeze
type = ALLOWD_OPTIONS.detect { |e| e == backup.procedure.storage_name.to_sym }
if type
  records = send(%s"Backup::Record::#{type.upcase}.all", :conditions => {:trigger => trigger})
end

или

if [:s3, :scp, :ftp, :sftp].include?(backup.procedure.storage_name.to_sym)
   records = __send__ (%s"Backup::Record::#{backup.procedure.storage_name.to_sym.upcase}.all", 
                       :conditions => {:trigger => trigger})
end

Вы можете использовать public_send() для дополнительной безопасности, если вы используете Ruby 1.9. Я посмотрел на рассматриваемый файл. Есть много случаев. Вы можете создать приватный метод, чтобы сделать что-то, как я написал выше, чтобы минимизировать дублирование

ALLOWD_OPTIONS = [:s3, :scp, :ftp ,:sftp].freeze

records = send_method_with_type("all", backup.procedure.storage_name.to_sym, 
                                       :conditions => {:trigger => trigger})

private

def send_method_with_type(method, type, *args)
  raise unless ALLOWD_OPTIONS.inlucde? type
  send(%s"Backup::Record#{type.upcase}.#{method}", args)
end
0 голосов
/ 22 декабря 2009

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

storage_method = backup.procedure.storage_name.upcase

records = eval("Backup::Record::#{storage_method}.all :conditions => {:trigger => trigger}"

Я не предполагаю, что это действительно правильно, но, надеюсь, это иллюстрирует мою точку зрения, что сжатый код не всегда лучше, чем явный код. Ruby может быть таким же опасным, как C: -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...