19

Java のメソッド署名:

public List<String> getFilesIn(List<File> directories)

ルビーの同様のもの

def get_files_in(directories)

Java の場合、型システムは、メソッドが何を期待し、何を提供するかについての情報を与えてくれます。Ruby の場合、何を渡すべきなのか、何を受け取ることになるのか、まったくわかりません。

Java では、オブジェクトは正式にインターフェースを実装する必要があります。Ruby では、渡されるオブジェクトは、ここで定義されたメソッドで呼び出されたメソッドに応答する必要があります。

これは非常に問題があるようです:

  1. 100% 正確で最新のドキュメントがあっても、Ruby コードは本質的にその実装を公開し、カプセル化を破る必要があります。「OO の純度」はさておき、これはメンテナンスの悪夢のようです。
  2. Ruby コードからは、何が返されているのかわかりません。基本的に実験するか、コードを読んで、返されたオブジェクトがどのメソッドに応答するかを調べる必要があります。

静的型付けとダック型付けについて議論するのではなく、契約によって設計する能力がほとんどない生産システムをどのように維持するかを理解しようとしています。

アップデート

このアプローチが必要とするドキュメントを介して、メソッドの内部実装の公開に実際に対処した人は誰もいません。インターフェースがないので、特定の型を想定していない場合、呼び出す可能性のあるすべてのメソッドを箇条書きにして、呼び出し元が何を渡すことができるかを知る必要はありませんか? それとも、これは実際には発生しない単なるエッジ ケースですか?

4

8 に答える 8

30

結局のところ、それはRubyget_files_inでは悪い名前です-説明させてください。

Java/C#/C++、特に目的の C では、関数の引数は name の一部です。ルビーではそうではありません。
これの凝った用語はMethod Overloadingで、コンパイラによって強制されます。

これらの用語で考えると、呼び出されるメソッドを定義しているだけで、get_files_in実際にファイルを取得する必要があることを言っているわけではありません。引数は名前の一部ではないため、それらに依存して識別できません。
ディレクトリ内のファイルを取得する必要がありますか? ドライブ?ネットワーク共有?これにより、上記のすべての状況で機能する可能性が開かれます。

ディレクトリに制限したい場合は、この情報を考慮に入れるために、メソッドを呼び出す必要がありますget_files_in_directoryDirectoryまたは、クラスのメソッドにすることもできます。これは、 Ruby が既に行っていることです

get_files戻り値の型に関しては、ファイルの配列を返していることが暗示されています。誰もが配列を使用するだけなので (そして、カスタム配列を作成した場合は、組み込みの配列から継承するように作成します)、それが や > などList<File>であることを心配する必要はありません。ArrayList<File

ファイルを 1 つだけ取得したい場合は、それを呼び出しget_fileますget_first_fileFileWrapper文字列だけでなくオブジェクトを返すなど、より複雑なことをしている場合は、本当に良い解決策があります。

# returns a list of FileWrapper objects
def get_files_in_directory( dir )
end

とにかく。Ruby では Java のようにコントラクトを強制することはできませんが、これはより広い意味でのサブセットです。つまり、Ruby では Java のように何も強制することはできません。Ruby のより表現力豊かな構文のおかげで、代わりに英語のようなコードをより明確に記述して、他の人にあなたのコントラクトが何であるかを伝えることができます (その結果、数千の山括弧を節約できます)。

私は、これが正味の勝利だと信じています。新たに見つけた空き時間を使って、いくつかの仕様とテストを作成し、1 日の終わりにははるかに優れた製品を生み出すことができます。

于 2008-10-07T03:26:12.373 に答える
8

Javaメソッドはより多くの情報を提供しますが、快適にプログラミングするのに十分な情報を提供しないと私は主張します。
たとえば、その文字列のリストは単なるファイル名ですか、それとも完全修飾パスですか?

それを考えると、Rubyが十分な情報を提供しないというあなたの議論はJavaにも当てはまります。
あなたはまだドキュメントを読んだり、ソースコードを見たり、メソッドを呼び出してその出力を見たりすることに依存しています(そしてもちろんまともなテスト)。

于 2008-10-07T09:08:25.147 に答える
6

私は Java コードを書いているときは静的型付けが大好きですが、Ruby コード (またはそのようなコード) でよく考えられた前提条件を主張できない理由はありません。(Ruby で) メソッド パラメーターの前提条件を本当に主張する必要がある場合は、ランタイム例外をスローしてプログラマー エラーを警告できる条件を喜んで記述します。私は次のように書くことで、静的型付けのようなものを自分自身に与えます。

def get_files_in(directories)
   unless File.directory? directories
      raise ArgumentError, "directories should be a file directory, you bozo :)"
   end
   # rest of my block
end

この言語が契約による設計を妨げているようには思えません。むしろ、これは開発者次第だと私には思えます。

(ところで、「bozo」は本当にあなたのものを指します:)

于 2008-10-07T03:16:35.107 に答える
5

ダックタイピングによるメソッド検証:

i = {}
=> {}
i.methods.sort
=> ["==", "===", "=~", "[]", "[]=", "__id__", "__send__", "all?", "any?", "class", "clear", "clone", "collect", "default", "default=", "default_proc", "delete", "delete_if", "detect", "display", "dup", "each", "each_key", "each_pair", "each_value", "each_with_index", "empty?", "entries", "eql?", "equal?", "extend", "fetch", "find", "find_all", "freeze", "frozen?", "gem", "grep", "has_key?", "has_value?", "hash", "id", "include?", "index", "indexes", "indices", "inject", "inspect", "instance_eval", "instance_of?", "instance_variable_defined?", "instance_variable_get", "instance_variable_set", "instance_variables", "invert", "is_a?", "key?", "keys", "kind_of?", "length", "map", "max", "member?", "merge", "merge!", "method", "methods", "min", "nil?", "object_id", "partition", "private_methods", "protected_methods", "public_methods", "rehash", "reject", "reject!", "replace", "require", "respond_to?", "select", "send", "shift", "singleton_methods", "size", "sort", "sort_by", "store", "taint", "tainted?", "to_a", "to_hash", "to_s", "type", "untaint", "update", "value?", "values", "values_at", "zip"]
i.respond_to?('keys')
=> true
i.respond_to?('get_files_in')  
=> false

その理由が理解できたら、メソッド シグネチャは関数内で動的にテストできるため、意味がありません。(これは、シグニチャー・マッチ・ベースの機能ディスパッチを行うことができないことが部分的に原因ですが、シグニチャーの無制限の組み合わせを定義できるため、より柔軟です)

 def get_files_in(directories)
    fail "Not a List" unless directories.instance_of?('List')
 end

 def example2( *params ) 
    lists = params.map{|x| (x.instance_of?(List))?x:nil }.compact 
    fail "No list" unless lists.length > 0
    p lists[0] 
 end

x = List.new
get_files_in(x)
example2( 'this', 'should', 'still' , 1,2,3,4,5,'work' , x )

より確実なテストが必要な場合は、動作駆動開発用のRSpecを試すことができます。

于 2008-10-07T03:13:10.043 に答える
3

簡単な答え:自動化された単体テストと適切な命名規則。

メソッドの適切な命名は不可欠です。メソッドに名前get_files_in(directory)を付けることで、メソッドが何を取得し、何を返すかについてユーザーにヒントを与えることにもなります。たとえば、Potatoオブジェクトが出てくるとは思いget_files_in()ません。意味がありません。ファイル名のリスト、またはより適切には、そのメソッドから File インスタンスのリストを取得することだけが理にかなっています。リストの具体的な型に関しては、何をしたいかによって、実際に返される List の型はそれほど重要ではありません。重要なのは、そのリストの項目を何らかの方法で列挙できることです。

最後に、そのメソッドに対する単体テストを記述して明示的にし、どのように機能するかの例を示します。そのため、get_files_in突然 Potato が返された場合、テストでエラーが発生し、最初の仮定が間違っていることがわかります。

于 2008-10-07T03:42:18.347 に答える
1

私は数年前にRuby用のdbcのようなもので中途半端な試みをしましたが、より包括的なソリューションで前進する方法について人々にいくつかのアイデアを与えるかもしれません:

https://github.com/justinwiley/higher-expectations

于 2011-03-22T00:11:05.363 に答える
1

API の一貫性と優れたドキュメントを必要とするのは、決してメンテナンスの悪夢ではなく、単なる別の作業方法です。

あなたの懸念は、動的言語は危険なツールであり、API の入出力契約を強制できないという事実に関連しているようです。実際には、静的を選択する方が安全に思えるかもしれませんが、両方の世界でできるより良いことは、返されるデータの型だけでなく (Java コンパイラーが検証できる唯一のものであり、強制)だけでなく、正確性と内部の仕組み(ブラックボックス/ホワイトボックステスト)でもあります。

余談ですが、Ruby についてはわかりませんが、PHP では @phpdoc タグを使用して、特定のメソッドによって返されるデータ型について IDE (Eclipse PDT) にヒントを与えることができます。

于 2008-10-07T03:09:24.573 に答える