75

オブジェクト指向の方法で干し草の山の針検索を実装する場合、基本的に3つの選択肢があります。

1. needle.find(haystack)

2. haystack.find(needle)

3. searcher.find(needle, haystack)

どちらが好きですか、そしてその理由は何ですか?

3番目のオブジェクトの導入を回避するため、2番目の選択肢を好む人もいます。ただし、少なくとも「現実の世界」をモデル化することが目標である場合は、3番目のアプローチがより概念的に「正しい」と感じざるを得ません。

この例のサーチャーなどのヘルパーオブジェクトを導入することはどのような場合に正当であると思いますか。また、いつそれらを回避する必要がありますか。

4

29 に答える 29

55

3 つのうち、私はオプション #3 を好みます。

単一責任の原則により、DTO やモデルに検索機能を追加したくありません。彼らの責任はデータであることであり、自分自身を見つけることではなく、針が干し草の山について知る必要も、干し草の山が針について知る必要もありません。

価値のあることですが、ほとんどの OO 実践者が #3 が最良の選択である理由を理解するには長い時間がかかると思います。私はオブジェクト指向を本当に理解する前に、おそらく 10 年間オブジェクト指向をやっていました。

@wilhelmtell、C ++は、そのようなシステムを実際に機能させるテンプレートの特殊化を備えた数少ない言語の1つです。ほとんどの言語では、汎用の「検索」メソッドは恐ろしい考えです。

于 2008-08-23T01:08:11.930 に答える
53

通常、アクションはアクションを実行しているものに適用する必要があります...この場合は干し草の山なので、オプション 2 が最も適切だと思います。

また、選択肢 3 よりも優れていると思われる 4 番目の選択肢もあります。

haystack.find(needle, searcher)

この場合、アクションの一部として検索する方法を提供できるため、操作対象のオブジェクトでアクションを維持できます。

于 2008-08-23T00:05:20.397 に答える
16

別の方法として、C++ の STL で使用されるアプローチがあります。

find(haystack.begin(), haystack.end(), needle)

これは、C++ が "in your face!" と叫ぶ良い例だと思います。OOPに。その考えは、OOP はいかなる種類の特効薬でもないということです。物事は、アクションの観点から最もよく説明される場合もあれば、オブジェクトの観点から説明される場合もあり、どちらでもない場合もあれば、両方の場合もあります。

Bjarne Stroustrup は TC++PL で、システムを設計するときは、効果的かつ効率的なコードの制約の下で現実を反映するよう努めるべきだと述べました。私にとって、これは何事も盲目的に従うべきではないことを意味します。手元にあるもの (干し草の山、針) と、私たちがいる状況 (検索、それが表現の内容です) について考えてください。

検索に重点が置かれている場合は、検索を重視するアルゴリズム (アクション) を使用します (つまり、干し草の山、海、砂漠、リンクされたリストに柔軟に適合します)。重点が干し草の山にある場合は、干し草の山オブジェクト内に find メソッドをカプセル化します。

とはいえ、時には迷ったり、選択をするのに苦労したりすることもあります。この場合、オブジェクト指向になります。後で気が変わった場合は、アクションをオブジェクトとクラスに分割するよりも、オブジェクトからアクションを抽出する方が簡単だと思います。

これらのガイドラインに従うと、コードがより明確になり、さらに美しくなります。

于 2008-08-23T01:05:14.213 に答える
15

オプション1は完全にアウトだと思います。コードは、それが何をするかを伝える方法で読む必要があります。オプション 1 では、この針が干し草の山を見つけに行くと思います。

干し草の山が針を含むことを意図している場合、オプション 2 は適切に見えます。ListCollections には常に ListItems が含まれるため、collection.find(item) を実行することは自然で表現力豊かです。

ヘルパー オブジェクトの導入は、次の場合に適していると思います。

  1. 問題のオブジェクトの実装を制御しません
    IE: search.find(ObsecureOSObject, file)
  2. オブジェクト IE 間に規則的または適切な関係はありません
    : nameMatcher.find(houses,trees.name)
于 2008-08-22T23:56:06.237 に答える
11

私はこれについてブラッドと一緒です。非常に複雑なシステムに取り組めば取り組むほど、オブジェクトを真に切り離す必要性を感じます。彼は正しい。針が干し草の山について何も知らなくてよいことは明らかなので、1 は間違いなくアウトです。しかし、干し草の山は針について何も知らないはずです。

干し草の山をモデル化する場合、それをコレクションとして実装するかもしれませんが、針のコレクションではなく、干し草やわらのコレクションとして実装します! ただし、干し草の山で物が失われることは考慮に入れますが、その物が正確に何であるかについては何も知りません。干し草の山にそれ自体でアイテムを検索させない方がよいと思います (とにかく、干し草の山がどれだけ賢いか)。私にとって正しいアプローチは、干し草の山にその中にあるもののコレクションを提示させることですが、わらでも干し草でも、干し草の山に本質を与えるものでもありません。

class Haystack : ISearchableThingsOnAFarm {
   ICollection<Hay> myHay;
   ICollection<IStuffSmallEnoughToBeLostInAHaystack> stuffLostInMe;

   public ICollection<Hay> Hay {
      get {
         return myHay;
      }
   }

   public ICollection<IStuffSmallEnoughToBeLostInAHayStack> LostAndFound {
      get {
        return stuffLostInMe;
      }
   }
}

class Needle : IStuffSmallEnoughToBeLostInAHaystack {
}

class Farmer {
  Search(Haystack haystack, 
                 IStuffSmallEnoughToBeLostInAHaystack itemToFind)
}

実際にはもっと多くのことを入力してインターフェースに抽象化しようとしていたのですが、自分がどれほどクレイジーになっているかに気づきました。大学のCSクラスにいるような気がした... :P

あなたはアイデアを得る。できるだけ疎結合にするのは良いことだと思いますが、少し夢中になっていたのかもしれません。:)

于 2008-08-23T01:52:05.867 に答える
6

NeedleとHaystackの両方がDAOである場合、オプション1と2は問題外です。

この理由は、DAOは、モデリングしている実世界のオブジェクトのプロパティの保持のみを担当し、getterメソッドとsetterメソッド(またはプロパティへの直接アクセス)のみを保持する必要があるためです。これにより、DAOをファイルにシリアル化したり、汎用比較/汎用コピーのメソッドを作成したりすることが容易になります。コードには、これらのヘルパーメソッドをスキップするための「if」ステートメントがすべて含まれていないためです。

これはオプション3を残すだけであり、ほとんどの場合、正しい動作に同意します。

オプション3にはいくつかの利点がありますが、最大の利点は単体テストです。これは、NeedleオブジェクトとHaystackオブジェクトの両方を簡単にモックアップできるのに対し、オプション1または2を使用した場合、検索を実行する前にNeedleまたはHaystackの内部状態を変更する必要があるためです。

次に、サーチャーが別のクラスにあるため、一般的な検索コードを含め、すべての検索コードを1か所にまとめることができます。一方、検索コードがDAOに配置された場合、一般的な検索コードは、複雑なクラス階層に格納されるか、とにかくSearcherHelperクラスとともに格納されます。

于 2008-09-08T03:47:16.733 に答える
5

これは、何が変化し、何が変わらないかに完全に依存します。

たとえば、私は(非OOP)フレームワークに取り組んでおり、針のタイプと干し草の山の両方によって検索アルゴリズムが異なります。needle.find(haystack)これにはオブジェクト指向環境でのダブルディスパッチが必要になるという事実は別として、どちらかを書くことも書くことも意味がないことも意味しますhaystack.find(needle)

一方、アプリケーションは、両方のクラスのいずれかに検出を喜んで委任することも、1つのアルゴリズムに完全に固執することもできます。その場合、決定は任意です。その場合、haystack.find(needle)干し草の山に調査結果を適用する方が論理的であると思われるため、この方法をお勧めします。

于 2008-08-22T23:54:48.720 に答える
5

オブジェクト指向の方法で干し草の山の針検索を実装する場合、基本的に 3 つの選択肢があります。

  1. needle.find(干し草の山)

  2. haystack.find(針)

  3. searcher.find(針、干し草の山)

どちらが好きですか、なぜですか?

間違っている場合は訂正してください。ただし、3 つの例すべてで、探している針への参照が既にあるため、これは、眼鏡が鼻の上に置かれているときに眼鏡を探すようなものではありませんか? :p

しゃれはさておき、干し草の山の責任が特定のドメイン内にあるとあなたが考えることに本当に依存していると思います。針を含むもの(本質的にコレクション)であるという意味で、私たちはそれを気にかけていますか?次に、haystack.find(needlePredicate) は問題ありません。それ以外の場合は、 farmBoy.find(predicate, haystack) の方が適切かもしれません。

于 2008-08-24T21:05:20.780 に答える
4

SICPの偉大な著者を引用すると、

プログラムは、人が読めるように書かれていなければならず、機械が実行するのは偶然に限られます

方法1と2の両方を手元に置いておくことを好みます。ruby を例にすると、.include?このように使用されます。

haystack.include? needle
=> returns true if the haystack includes the needle

ただし、純粋に読みやすさの理由から、ひっくり返したい場合があります。Ruby にはメソッドが付属していませんがin?、ワンライナーなので、私はよく次のようにします。

needle.in? haystack
=> exactly the same as above

干し草の山、または検索操作を強調することが「より重要」である場合、私はinclude?. ただし、多くの場合、干し草の山も検索も気にする必要はなく、オブジェクトが存在するだけです。この場合in?、プログラムの意味をよりよく伝えていると思います。

于 2008-08-23T04:05:52.253 に答える
3

最初の 2 つのフレーバーのいずれかが理にかなっている状況を考えることができます。

  1. Knuth-Morris-Pratt アルゴリズムのように針に前処理が必要な場合needle.findIn(haystack)(またはpattern.findIn(text)) は意味があります。これは、針オブジェクトが、アルゴリズムが効果的に機能するために作成された中間テーブルを保持するためです。

  2. トライのように、干し草の山に前処理が必要な場合は、haystack.find(needle)(またはwords.hasAWordWithPrefix(prefix)) の方がうまく機能します。

上記のどちらの場合でも、needle または haystack のいずれかが検索を認識しています。また、両者はお互いを認識しています。針と干し草の山がお互いや検索を認識しないようにする場合は、searcher.find(needle, haystack)これが適切です。

于 2008-09-01T06:04:30.990 に答える
3

それはあなたの要件に依存します。

たとえば、検索者の特性 (検索者の強さ、視力など) を気にしない場合は、haystack.find(needle) が最もクリーンなソリューションであると言えます。

ただし、サーチャーのプロパティ (またはその他のプロパティ) を気にする場合は、haystack コンストラクターまたは関数のいずれかに ISearcher インターフェイスを挿入して、それを容易にします。これにより、オブジェクト指向設計 (干し草の山には針があります) と制御の反転/依存性注入 (「検索」機能の単体テストが容易になります) の両方がサポートされます。

于 2008-08-23T01:17:31.597 に答える
2

本当に2と3のミックス。

一部の干し草の山には、特別な検索戦略がありません。この例は配列です。何かを見つける唯一の方法は、最初から始めて、必要なものが見つかるまで各アイテムをテストすることです。

この種のことには、おそらく無料の関数が最適です(C ++のように)。

一部の干し草の山には、特殊な検索戦略を課すことができます。配列を並べ替えることができ、たとえば、バイナリ検索を使用できます。無料の関数(またはsortとbinary_searchなどの無料の関数のペア)がおそらく最良のオプションです。

一部の干し草の山には、実装の一部として統合された検索戦略があります。たとえば、連想コンテナ(ハッシュまたは順序付けされたセットとマップ)はすべて機能します。この場合、検索はおそらく必須のルックアップメソッドであるため、おそらくhaystack.find(needle)などのメソッドである必要があります。

于 2008-08-28T20:34:47.310 に答える
2

haystack.find(needle) ですが、サーチャー フィールドが必要です。

依存性注入が大流行していることは知っているので、@Mike Stone の haystack.find(needle, searcher) が受け入れられたことは驚くことではありません。しかし、私は同意しません。どのサーチャーを使用するかの選択は、干し草の山のための決定のように私には思えます。

2 つの ISearcher について考えてみましょう。MagneticSearcher はボリュームを反復処理し、磁石の強さと一致する方法で磁石を移動およびステップします。QuickSortSearcher は、サブパイルの 1 つで針が明らかになるまで、スタックを 2 つに分割します。サーチャの適切な選択は、干し草の山がどのくらい大きいか (たとえば、磁場と比較して)、針が干し草の山にどのように入ったか (つまり、針の位置が本当にランダムなのか、偏っているのか?) などによって異なります。

haystack.find(needle, searcher) がある場合、「最適な検索戦略を選択することは、干し草の山のコンテキスト外で行うのが最適です」と言っているのです。それが正しい可能性は低いと思います。「干し草の山は自分自身を探す最善の方法を知っている」可能性が高いと思います。セッターを追加しても、オーバーライドする必要がある場合やテストのためにサーチャーを手動で挿入できます。

于 2008-09-16T03:45:22.727 に答える
2

この質問に対する答えは、ソリューションが実装されているドメインによって異なります。
たまたま物理的な干し草の山で物理的な検索をシミュレートした場合、次のクラスがある可能性があります

  • スペース
  • ストロー
  • シーカー

空間
は、どのオブジェクトがどの座標に配置されているかを認識しており
、自然の法則を実装しています (エネルギーの変換、衝突の検出など)。

ストロー
はスペースにあり、
力に反応し ます

シーカー
は空間と相互作用します:
    手を動かし、磁場を適用し、干し草を燃やし、X 線を照射し、針を探します...

したがって  seeker.find(needle, space)   、または   seeker.find(needle, space, strategy)

干し草の山は、たまたま針を探している場所にあります。一種の仮想マシン (マトリックスと考えてください) としてスペースを抽象化すると、スペースの代わりにヘイスタックを使用して上記を取得できます(ソリューション 3/3b) :

seeker.find(needle, haystack)     また     seeker.find(needle, haystack, strategy)

しかし、マトリックスはドメインであり、針が他の場所にない場合にのみ、干し草の山に置き換える必要があります.

繰り返しになりますが、それは単なるアナロジーでした。興味深いことに、これはまったく新しい方向性への心を開きます:
1. そもそもなぜ針を緩めたのですか? 紛失しないようにプロセスを変更できますか?
2. なくした針を見つけなければなりませんか、それとも別の針を手に入れて最初の針を忘れることができますか? (その後、しばらくして針が溶ければいいのですが)
3. 定期的に針をなくし、もう一度見つける必要がある場合

  • 自分自身を見つけることができる針を作ります。たとえば、針は定期的に自問します。答えが「はい」の場合、GPS で計算された位置を誰かに送信するか、ビープ音などを鳴らし始めます。
    needle.find(space)またはneedle.find(haystack)    (解決策 1)

  • 各ストローにカメラ付きの干し草の山を設置します。その後、干し草の山に最近針を見たかどうかを尋ねることができます:
    haystack.find(needle) (解決策 2)

  • 針に RFID タグを取り付けて、簡単に三角測量を行うことができます

あなたの実装では、針と干し草の山、そしてほとんどの場合、何らかのレベルでマトリックスを作成したと言っているだけです

したがって、ドメインに応じて決定します。

  • 干し草の山に針を入れるのが目的ですか? 次に、解決策 2 に進みます。
  • 針がどこかに落ちてしまうのは当然ですか?次に、解決策 1 に進みます。
  • うっかり干し草の山に針が落ちてしまいませんか?次に、解決策 3 に進みます (または、別の回復戦略を検討してください)。
于 2008-09-13T19:56:31.073 に答える
2

簡単:干し草の山を燃やせ!その後、針だけが残ります。また、磁石を試すこともできます。

より難しい質問: 針のプールから特定の針をどのように見つけますか?

答え: それぞれにスレッドを作成し、スレッドの各ストランドのもう一方の端をソートされたインデックス (つまり、ポインター) に接続します。

于 2008-08-28T18:35:10.013 に答える
1

コードは特定の針を見つけようとしていますか、それとも任意の針を見つけようとしていますか?ばかげた質問のように聞こえますが、問題は変わります。

特定の針を探すと、質問のコードは理にかなっています。針を探すともっと似ているでしょう

needle = haystack.findNeedle()

また

needle = searcher.findNeedle(haystack)

いずれにせよ、私はそのクラスのサーチャーを持つことを好みます。干し草の山は検索方法を知りません。CSの観点からは、それはあなたが望まないたくさんのがらくたを持った単なるデータストアです。

于 2008-09-08T02:56:40.867 に答える
1

また、選択肢 3 よりも優れていると思われる 4 番目の選択肢もあります。

haystack.find(needle, searcher)

あなたの言いたいことはわかりますがsearcher、干し草の山以外の種類のオブジェクトを検索し、その中の針以外のものを見つけることを可能にする検索インターフェースを実装するとどうなりますか?

インターフェイスは、さまざまなアルゴリズムで実装することもできます。たとえば、次のようになります。

binary_searcher.find(needle, haystack)
vision_searcher.find(pitchfork, haystack)
brute_force_searcher.find(money, wallet)

しかし、他の人がすでに指摘しているように、これは、実際に複数の検索アルゴリズムまたは複数の検索可能または検索可能なクラスがある場合にのみ役立つと思います。haystack.find(needle)そうでない場合は、その単純さのために優れていることに同意するので、「正確さ」をいくらか犠牲にしても構わないと思っています。

于 2008-08-24T19:33:11.000 に答える
1

干し草の山は物を含むことができます 物の 1 つのタイプは針のファインダーであり、物の検索を担当するものです ファインダーは物を見つける場所のソースとして物の山を受け入れることができます ファインダーは見つける必要がある物の説明を受け入れることもできます

したがって、できれば、柔軟なソリューションの場合は次のようにします。 IStuff インターフェイス

Haystack = IList<IStuff> Needle : IStuff

Finder .Find(IStuff stuffToLookFor, IList<IStuff> stuffsToLookIn)

この場合、ソリューションは針と干し草の山だけに縛られることはありませんが、インターフェイスを実装するすべてのタイプで使用できます

海で魚を見つけたいなら、できます。

var results = Finder.Find(魚、海)

于 2008-09-09T16:56:29.793 に答える
1

針オブジェクトへの参照がある場合、なぜそれを検索するのですか? :) 問題のドメインとユースケースは、干し草の山 (list.indexOf(element) から取得できるものなど) 内の針の正確な位置は必要ないことを示しています。針が必要なだけです。そして、あなたはまだそれを持っていません。だから私の答えはこのようなものです

Needle needle = (Needle)haystack.searchByName("needle");

また

Needle needle = (Needle)haystack.searchWithFilter(new Filter(){
    public boolean isWhatYouNeed(Object obj)
    {
        return obj instanceof Needle;
    }
});

また

Needle needle = (Needle)haystack.searchByPattern(Size.SMALL, 
                                                 Sharpness.SHARP, 
                                                 Material.METAL);

さまざまな検索戦略に基づく可能な解決策が他にもあることに同意するので、searcher を紹介します。これについてはコメントが多かったのでここでは割愛します。私のポイントは、上記のソリューションはユースケースを忘れているということです-すでに参照している場合、何かを検索するポイントは何ですか? 最も自然なユースケースでは、まだ針がないため、可変針は使用しません。

于 2008-11-05T19:21:18.070 に答える
1

個人的には2番目の方法が好きです。私の理由は、私が扱ってきた主要な API がこのアプローチを使用しており、それが最も理にかなっているからです。

物のリスト (干し草の山) がある場合は、針を検索 (find()) します。

于 2008-08-22T23:55:29.983 に答える
1

干し草の山は針のことを知っているべきではなく、針は干し草の山について知っているべきではありません。検索者は両方について知る必要がありますが、干し草の山が自分自身を検索する方法を知っている必要があるかどうかは、本当の争点です。

したがって、2 と 3 を組み合わせて使用​​します。干し草の山は他の誰かにそれを検索する方法を伝えることができ、検索者はその情報を使用して干し草の山を検索できる必要があります。

于 2008-08-28T18:44:17.090 に答える
1

@ピーター・マイヤー

あなたはアイデアを得る。できるだけ疎結合にするのは良いことだと思いますが、少し夢中になっていたのかもしれません。:)

エラー...ええ...私はそのIStuffSmallEnoughToBeLostInAHaystackようなものは危険信号だと思います:-)

于 2008-08-23T04:09:00.047 に答える
1
haystack.magnet().filter(needle);
于 2008-08-24T22:58:36.250 に答える
1
class Haystack {//whatever
 };
class Needle {//whatever
 }:
class Searcher {
   virtual void find() = 0;
};

class HaystackSearcher::public Searcher {
public:
   HaystackSearcher(Haystack, object)
   virtual void find();
};

Haystack H;
Needle N;
HaystackSearcher HS(H, N);
HS.find();
于 2008-08-28T19:14:37.253 に答える
1

Brad Wilson は、オブジェクトは単一の責任を持つべきだと指摘しています。極端な場合、オブジェクトには 1 つの責任があり、状態はありません。その後、それは... 関数になります。

needle = findNeedleIn(haystack);

または、次のように書くこともできます。

SynchronizedHaystackSearcherProxyFactory proxyFactory =
    SynchronizedHaystackSearcherProxyFactory.getInstance();
StrategyBasedHaystackSearcher searcher =
    new BasicStrategyBasedHaystackSearcher(
        NeedleSeekingStrategies.getMethodicalInstance());
SynchronizedHaystackSearcherProxy proxy =
    proxyFactory.createSynchronizedHaystackSearcherProxy(searcher);
SearchableHaystackAdapter searchableHaystack =
    new SearchableHaystackAdapter(haystack);
FindableSearchResultObject foundObject = null;
while (!HaystackSearcherUtil.isNeedleObject(foundObject)) {
    try {
        foundObject = proxy.find(searchableHaystack);
    } catch (GruesomeInjuryException exc) {
        returnPitchforkToShed();  // sigh, i hate it when this happens
        HaystackSearcherUtil.cleanUp(hay); // XXX fixme not really thread-safe,
                                           // but we can't just leave this mess
        HaystackSearcherUtil.cleanup(exc.getGruesomeMess()); // bug 510000884
        throw exc; // caller will catch this and get us to a hospital,
                   // if it's not already too late
    }
}
return (Needle) BarnyardObjectProtocolUtil.createSynchronizedFindableSearchResultObjectProxyAdapterUnwrapperForToolInterfaceName(SimpleToolInterfaces.NEEDLE_INTERFACE_NAME).adapt(foundObject.getAdaptable());
于 2009-11-20T12:19:09.703 に答える
0

別の可能な方法は、haystack などの Searchable オブジェクトと、needle などの ToBeSearched オブジェクトの 2 つのインターフェイスを作成することです。だから、それはこのように行うことができます

public Interface IToBeSearched
{}

public Interface ISearchable
{

    public void Find(IToBeSearched a);

}

Class Needle Implements IToBeSearched
{}

Class Haystack Implements ISearchable
{

    public void Find(IToBeSearched needle)

   {

   //Here goes the original coding of find function

   }

}
于 2008-11-13T16:19:51.470 に答える
0
haystack.iterator.findFirst(/* pass here a predicate returning
                               true if its argument is a needle that we want */)

iteratorfindFirst(fun: T => Boolean)ジョブを実行する共通メソッドを持つコレクションを使用して、任意の不変コレクションへのインターフェイスにすることができます。干し草の山が不変である限り、有用なデータを「外部」から隠す必要はありません。そしてもちろん、自明ではないカスタム コレクションの実装と、haystack. 分割して征服しますか?

于 2012-08-13T00:42:37.313 に答える
0

間違いなく3番目、IMHO。

干し草の山の中の針の問題は、他のオブジェクトの大規模なコレクションから 1 つのオブジェクトを見つけようとする試みの例です。これは、複雑な検索アルゴリズム (磁石または (より可能性が高い) 子プロセスを含む可能性があります) が必要であることを示していますが、そうではありません。干し草の山にスレッド管理や複雑な検索の実装を期待するのは理にかなっています。

ただし、検索オブジェクトは検索専用であり、高速バイナリ検索のために子スレッドを管理する方法、または検索対象要素のプロパティを使用して領域を狭める方法を知っていることが期待できます (つまり、鉄のアイテムを見つけるための磁石)。 .

于 2008-11-13T16:05:20.140 に答える