1

私はRubyを初めて使用し、主にC#とActionScript 3(他の言語の中でも)から来ています。機能の抽象化に興味があります。具体的には、RubyのFTPおよびSFTPライブラリをラップおよび抽象化します。

私は周りを探していて、バックアップと呼ばれる宝石に出くわしました。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

エレガントにできる方法はいくつかあると思います。1つは、上記で提案したTKとして送信を使用することです。もう1つは、既存のメソッドが見つからない場合にRubyが呼び出すメソッド「method_missing」を使用することです。

メタプログラミングRubyは、これらのオプションの両方を十分にカバーしています。幸いなことに、それはオンラインの無料サンプルの章にあります(詳細を知りたい場合は、この本をお勧めします)。

コードスニペットを提供しなかったことをお詫びしますが、それを読んで、それが役立つかどうかを確認してください。

于 2009-12-22T18:55:07.870 に答える
1

ほとんどの場合、OO 言語のケース式は、ポリモーフィズムを正しく使用していないことを示しています。この場合、私はそれを次のようにします:

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

Wherestorage_classは適切なクラスを返します。(実際にstorage_classは、バックアップ自体のプロパティを作成したいのですが、このライブラリの設計においてそれが実用的かどうかはわかりません。)

于 2009-12-23T19:04:33.957 に答える
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

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