1

我是 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 上查看完整源代码

它到处都是 case/when 语句!如果我在 C# 中对此进行攻击,我会编写一个协议接口(或抽象类)并让 FTP 和 SFTP 实现它。然后我的客户端类将只传递一个协议实例而不关心实现。零开关/外壳。

在用 Ruby 编码时,我会很感激在这种情况下的最佳实践的一些指导。

4

5 回答 5

6

你也可以在 Ruby 中这样做

由于动态类型,Ruby 不需要接口。就此而言,它不需要原型、签名或模板,甚至子类,虽然存在,但并不是绝对必要的。

而当我说“不需要”时,我只是说您所指的设计模式可以直接在 Ruby 中实现。因为在“编译时”没有强制执行任何调用限制,所以任何依赖于接口或任何多态性的设计模式都可以在 Ruby 中直接使用。

是的,该包似乎没有充分利用可能的抽象,但也许(a)它并不重要,只要它有效。毕竟,您不需要输入它,或者 (b) 使用的简单组合模式有一些并非立即显而易见的好处。

于 2009-12-22T07:01:10.153 回答
2

我认为有几种方法可以优雅地做到这一点。一是使用上面建议的 TK 发送。另一种是使用“method_missing”,Ruby 在找不到现有方法时调用的方法。

元编程 Ruby很好地涵盖了这两个选项。幸运的是,它位于在线免费示例章节中(如果您想了解更多信息,我推荐这本书)。

很抱歉没有给你一个代码片段,但请仔细阅读它,看看它是否有帮助。

于 2009-12-22T18:55:07.870 回答
1

大多数时候,OO 语言中的 case 表达式表明您没有正确使用多态性。在这种情况下,我会将其设置为:

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

Wherestorage_class返回相应的类。(实际上,我更愿意为storage_class备份本身创建一个属性,但我不知道这在这个库的设计中是否实用。)

于 2009-12-23T19:04:33.957 回答
0

公平地说,上面的代码非常明确。除了将其实现为对象的层次结构之外,您还可以发疯并做类似的事情

storage_method = backup.procedure.storage_name.upcase

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

我并不是说这实际上是正确的,但希望它能说明我的观点,即简洁的代码并不总是比显式的代码更好。Ruby 可能和 C 一样危险 :-)

于 2009-12-22T10:07:58.540 回答
0

可能有更好的方法,但这是我的镜头。

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

如果您使用的是 Ruby 1.9,则可以使用public_send()额外的安全性。我查看了有问题的文件。有很多案例陈述。您可以创建一个私有方法来执行我上面写的操作以最大程度地减少重复。

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
于 2009-12-22T17:30:50.630 回答