7

私は Java プログラマーのチームで働いています。私の同僚の 1 人が、「型フィールドを追加するだけ」(usu.「文字列型」) のようなことをするように提案することがあります。またはコードは " " を含んだ状態でコミットされますif (foo instanceof Foo){...} else if( foo instanceof Bar){...}

Josh Bloch の「タグ付きクラスは適切なクラス階層の模倣に過ぎない」という忠告にもかかわらず、この種のことに対する私の 1 行の応答は何ですか? そして、どうすればコンセプトをより真剣に説明できるでしょうか?

コンテキストがJavaであることは明らかです-検討中のオブジェクトのタイプは、集合的な顔のすぐ前にあります-IOW:「クラス」、「列挙型」、または「インターフェース」などの直後の単語。

しかし、(その場で)実証または定量化するのが難しいことは別として、「コードをより複雑にする」ことを除けば、「(多かれ少なかれ)強く型付けされた言語でのダックタイピングは愚かな考えです。より深い設計病理学を示唆していますか?

4

11 に答える 11

8

実際、あなたはその場でそれをかなりうまく言いました。

真実は、櫛の「インスタンス」はほとんどの場合悪い考えです (たとえば、マーシャリングまたはシリアル化を行っているとき、短い間隔ですべての型情報が手元にない場合に発生する例外です)。 、それ以外の場合、それは悪いクラス階層の兆候です。

それが悪い考えであることがわかる方法は、それがコードをもろくすることです: それを使用し、型の階層が変更された場合、そのインスタンスの櫛が発生するすべての場所で壊れる可能性があります。さらに、強力な型付けの利点が失われます。コンパイラは、事前にエラーをキャッチすることによってあなたを助けることはできません. (これは、C の型キャストによって引き起こされる問題にいくぶん類似しています。)

アップデート

コメントからは、私がはっきりしていないように見えるので、これを少し拡張させてください。C で型キャストを使用する理由、またはinstanceof、「あたかも」と言いたいからです: これfoobar. さて、C では、実行時の型情報がまったくないため、ネットなしで作業しているだけです。何かを型キャストすると、生成されたコードは、そのアドレスが何があっても特定の型を含んでいるかのように扱います。 、黙って何かを破壊するのではなく、実行時エラーが発生することを願うだけです。

ダックタイピングはそれを標準に引き上げるだけです。Ruby、Python、または Smalltalk のような動的で弱い型付けの言語では、すべてが型付けされていない参照です。実行時にメッセージを送信し、何が起こるかを確認します。特定のメッセージを理解すると、「アヒルのように歩き」、それを処理します。

これは非常に便利で便利です。Python でジェネレータ式を変数に代入したり、Smalltalk でブロックを変数に代入したりするなどの素晴らしいハックが可能になるからです。ただし、厳密に型指定された言語がコンパイル時にキャッチできる実行時のエラーに対して脆弱であることを意味します。

Java のような厳密に型指定された言語では、ダック タイピングをまったく厳密に行うことはできません。何かをどの型として扱うかをコンパイラに伝える必要があります。型キャストを使用してダックタイピングのようなものを取得できるため、次のようなことができます

Object x;   // A reference to an Object, analogous to a void * in C

// Some code that assigns something to x

((FoodDispenser)x).dropPellet();          // [1]

// Some more code

((MissleController)x).launchAt("Moon");   // [2]

FoodDispenser実行時には、x がat [1] またはMissleControllerat [2]の一種である限り問題ありません。そうでなければブーム。というか案外ノーブーム

あなたの説明の中で、あなたはelse ifとのくしを使って身を守りますinstanceof

 Object x ;

 // code code code 

 if(x instanceof FoodDispenser)
      ((FoodDispenser)x).dropPellet();
 else if (x instanceof MissleController )
      ((MissleController)x).launchAt("Moon");
 else if ( /* something else...*/ ) // ...
 else  // error

これで実行時エラーから保護されましたが、後で適切な処理を行う責任がありelseます。

しかしここで、'x' が 'FloorWax' と 'DessertTopping' の型を取ることができるように、コードを変更したとします。ここで、すべてのコードを調べて、そのコームのすべてのインスタンスを見つけて変更する必要があります。現在、コードは「脆弱」です。要件の変更は、多くのコードの変更を意味します。OO では、コードの脆さを軽減しようと努力しています。

OO の解決策は、代わりにポリモーフィズムを使用することです。これは一種の制限されたダック タイピングと考えることができます。何かが信頼して実行できるすべての操作を定義しています。これを行うには、下位クラスのすべてのメソッドを持つ上位クラス (おそらく抽象クラス) を定義します。Java では、このようなクラスは「インターフェース」と表現するのが最適ですが、クラスのすべてのタイプ プロパティを備えています。実際、インターフェイスは、特定のクラスが別のクラスであるかのように動作することを信頼できるという約束であると見なすことができます。

  public interface VeebleFeetzer { /* ... */ };
  public class FoodDispenser implements VeebleFeetzer { /* ... */ }
  public class MissleController implements VeebleFeetzer { /* ... */ }
  public class FloorWax implements VeebleFeetzer { /* ... */ }
  public class DessertTopping implements VeebleFeetzer { /* ... */ }

あとは、VeebleFeetzer への参照を使用するだけで、コンパイラがそれを計算してくれます。VeebleFeetzer のサブタイプである別のクラスをたまたま追加した場合、コンパイラはメソッドを選択し、掘り出し物の引数をチェックします。

VeebleFeetzer x;   // A reference to anything 
                   // that implements VeebleFeetzer

// Some code that assigns something to x

x.dropPellet();

// Some more code

x.launchAt("Moon");

 
    
于 2009-03-31T05:33:23.067 に答える
6

これは単なる適切なオブジェクト指向スタイルであるため、ダック タイピングではありません。実際、クラス A をサブクラス化し、クラス B で同じメソッドを呼び出して、別のことを実行させることが、言語における継承の全体的なポイントです。

オブジェクトの型を常にチェックしている場合は、頭が良すぎるか (ダックタイピングの愛好家が好むのは、それほど壊れにくい形を除いて、このような巧妙さだと思います)、オブジェクトの基本を受け入れていないかのどちらかです。指向のプログラミング。

于 2009-03-31T05:29:31.937 に答える
3

うーん...

タグ付けされたクラスとダックタイピングは 2 つの異なる概念ですが、必ずしも相互に排他的ではありません。

クラスでタグを使用して型を定義したいという衝動に駆られた場合、私見ですが、抽象クラスがクラスの親子関係が試みる実装の詳細を知る必要がある概念的なブリードの明確な歌であるため、クラス階層を修正する必要があります。隠れる。正しいパターンを使用していますか? 言い換えれば、自然にサポートされないパターンで行動を強制しようとしていますか?

ダックタイピングとは、パラメーターインスタンスで必要なメソッドが定義されている限り、メソッドが任意の型を受け入れることができる型を大まかに定義する機能です。次に、メソッドはパラメーターを使用し、インスタンスの親性をあまり気にせずに必要なメソッドを呼び出します。

ここで...臭いヒントは、チャーリーが指摘したように、instanceofの使用です。static キーワードやその他の悪臭を放つキーワードと同じように、それらが表示されるたびに、「ここで正しいことを行っているか?」と尋ねる必要があります。これらは継承的に間違っているわけではありませんが、よくないまたは不適切な OO デザインをハッキングするために使用されます。

于 2009-03-31T05:48:31.437 に答える
2

私の 1 行の応答は、OOP の主な利点の 1 つであるポリモーフィズムを失うということです。これにより、新しいコードを開発する時間が短縮されます (開発者は新しいコードを開発するのが大好きなので、それはあなたの議論に役立つはずです :-)

新しい型を既存のシステムに追加するときに、どのインスタンスを構築するかを考え出す以外に、ロジックを追加する必要がある場合、Java では何か間違ったことをしています (新しいクラスが単にドロップイン置換であると仮定すると)。別の場合)。

一般に、Java でこれを処理する適切な方法は、コードをポリモーフィックに保ち、インターフェイスを利用することです。したがって、別の変数を追加したり、instanceof を実行したい場合はいつでも、おそらく代わりにインターフェイスを実装する必要があります。

彼らにコードを変更するよう説得できれば、インターフェースを既存のコードベースに後付けするのは非常に簡単です。さらに言えば、時間をかけて instanceof を使用してコードを作成し、それをリファクタリングしてポリモーフィックにします。前後のバージョンを見て比較していただけると、より分かりやすいです。

于 2009-03-31T07:03:08.980 に答える
2

同僚に、SOLID の 5 つの柱の 1 つである Liskov 代替原理を紹介するとよいでしょう。

リンク:

于 2009-03-31T07:04:10.757 に答える
1

私は一般的にPythonのようなダックタイプの言語のファンですが、Javaであなたの問題を見ることができます.

このコードで使用されるすべてのクラスを作成している場合、コードが直接継承 (または実装) できない場合を考慮する必要がないため、ダックタイプする必要はありません。インターフェイスまたはその他の統合抽象化。

ダックタイピングの欠点は、コードで実行する単体テストのクラスが余分にあることです。新しいクラスが予想とは異なる型を返し、その後、残りのコードが失敗する可能性があります。したがって、ダックタイピングは後方への柔軟性を可能にしますが、テストには多くの前向きな思考が必要です。

要するに、キャッチ 少数 (簡単) ではなく、キャッチ オール (難しい) があります。それが病理だと思います。

于 2009-03-31T05:30:19.250 に答える
1

クラス階層を設計して使用するのではなく、なぜ「クラス階層を模倣する」のですか? リファクタリング方法の 1 つは、"switch"es (チェーンされた if はほとんど同じ) をポリモーフィズムに置き換えることです。ポリモーフィズムがよりクリーンなコードにつながるのに、なぜスイッチを使用するのでしょうか?

于 2009-03-31T05:31:05.410 に答える
1

「強く型付けされた言語でのダックタイピング」と言うとき、実際には「静的に型付けされた言語で (サブタイプ) ポリモーフィズムを模倣する」ことを意味します。

動作を含まないデータ オブジェクト (DTO) がある場合は、それほど悪くはありません。本格的な OO モデルがある場合 (これが本当に当てはまるかどうか自問してください)、必要に応じて言語によって提供されるポリモーフィズムを使用する必要があります。

于 2009-03-31T07:43:45.157 に答える
0

表題の質問に答える 2 つの引数:

1) Java は「一度書けばどこでも実行できる」と想定されているため、1 つの階層用に記述されたコードは、どこかで環境を変更したときに RuntimeExceptions をスローするべきではありません。(もちろん、この規則には例外もあります。語呂合わせです。)

2) Java JIT は、特定のシンボルが 1 つのタイプおよび 1 つのタイプのみでなければならないという認識に依存する、非常に積極的な最適化を実行します。これを回避する唯一の方法は、キャストすることです。

他の人が述べたように、あなたの「インスタンス」は私がここで答えた質問と一致しません。ダックでも静的でも、どのタイプでも、あなたが説明した問題が発生する可能性があります。それに対処するためのより良いOOPの方法があります。

于 2009-03-31T06:12:09.040 に答える
0

instanceof の代わりに Method- と Strategy-Pattern を使用できます。これらを組み合わせることで、コードは以前よりもはるかに見栄えがよくなります...

于 2009-03-31T07:06:54.340 に答える