私はいくつかの回答を見て、Googleで検索しましたが、役立つ情報は見つかりませんでした(つまり、厄介な副作用はありません)。
私の問題は、要約すると、オブジェクトがあり、それに対して長い一連の操作を実行する必要があることです。自動車をつくるような組み立てラインのようなものだと思います。
これらのオブジェクトはメソッドオブジェクトと呼ばれると思います。
したがって、この例では、ある時点でCarWithoutUpholsteryがあり、その上でinstallBackSeat、installFrontSeat、installWoodenInsertsを実行する必要があります(操作は相互に干渉せず、並列で実行されることもあります)。これらの操作はCarWithoutUpholstery.worker()によって実行され、CarWithUpholsteryとなる新しいオブジェクトを生成します。このオブジェクト上で、おそらくcleanInsides()、verifyNoUpholsteryDefects()などを実行します。
単一フェーズでの操作は既に独立しています。つまり、私はすでに、それらのサブセットを任意の順序で実行できるように取り組んでいます(前部座席と後部座席は任意の順序でインストールできます)。
私のロジックは現在、実装を簡単にするためにリフレクションを使用しています。
つまり、CarWithoutUpholsteryを取得すると、オブジェクトは、performSomething()と呼ばれるメソッドについてそれ自体を検査します。その時点で、これらのメソッドをすべて実行します。
myObject.perform001SomeOperation();
myObject.perform002SomeOtherOperation();
...
エラーやものをチェックしながら。操作の順序は重要ではありませんが、結局、何らかの順序が重要であることがわかった場合に備えて、辞書式順序を割り当てました。これはYAGNIと矛盾しますが、コストはごくわずか(単純なsort())で、大規模なメソッドの名前変更(またはメソッドの配列など、テストを実行する他のメソッドを導入すること)を節約できます。
別の例
車を製造する代わりに、誰かに関する秘密警察の報告書を編集し、それを私の悪の支配者に提出しなければならないとしましょう。最後のオブジェクトはReadyReportです。それを構築するために、基本的な情報(名前、姓、配偶者...)を収集することから始めます。これは私のフェーズAです。配偶者の有無に応じて、フェーズB1またはB2に進み、1人または2人の性別データを収集する必要があります。これは、ナイトライフ、ストリートカム、セックスショップの売上レシートなどを制御するさまざまなEvil Minionsに対するいくつかの異なるクエリで構成されています。などなど。
被害者が家族を持たない場合、私はGetInformationAboutFamilyフェーズに入ることさえしませんが、そうする場合、私が最初に父親、母親、または兄弟(もしあれば)をターゲットにするかどうかは関係ありません。しかし、FamilyStatusCheckを実行していない場合はそれを行うことができません。したがって、これは以前のフェーズに属します。
すべてうまくいきます...
- 追加の操作が必要な場合は、プライベートメソッドを追加するだけです。
- 操作がいくつかのフェーズに共通している場合、スーパークラスから継承させることができます。
- 操作はシンプルで自己完結型です。ある操作の値が他の操作で必要になることはありません(実行する操作は別のフェーズで実行されます)。
- 作成者オブジェクトがそもそもこれらの条件を検証していなかった場合、それらは存在することさえできなかったため、将来のオブジェクトは多くのテストを実行する必要はありません。つまり、ダッシュボードにインサートを配置し、ダッシュボードをクリーニングし、ダッシュボードを確認するときに、ダッシュボードが実際にそこにあることを確認する必要はありません。
- 簡単にテストできます。私は簡単に部分オブジェクトをモックして、その上で任意のメソッドを実行でき、すべての操作は確定的なブラックボックスです。
...だが...
問題は、メソッドオブジェクトの1 つに最後の操作を1つ追加したときに発生しました。これにより、モジュール全体が必須の複雑度インデックス(「N未満のプライベートメソッド」)を超えました。
私はすでにこの問題を2階で取り上げており、この場合、豊富なプライベートメソッドは災害の兆候ではないことを示唆しています。複雑さはあるが、しかし操作はので、それがありますされ、複雑な、そして実際に、それはすべてが複雑ではない-それだけだ長いです。
Evil Overlordの例を使用すると、私の問題は、Evil Overlord(別名拒否されるべきではない彼)がすべての食事情報を要求したことです。所有者など、そして悪(サブ)オーバーロード- 否定されるべきではない彼としてよく知られている-は、GetDietaryInformationフェーズで実行するクエリが多すぎると不平を言っています。
注:私はいくつかの観点からこれがまったく問題ではないことを認識しています(考えられるパフォーマンスの問題などを無視します)。起こっていることは、特定の測定基準が不幸であるということだけであり、それには正当化の余地があります。
何だと思い、私が行うことができます
最初のものを除いて、これらのオプションはすべて実行可能であり、防御可能だと思います。
- 私はこっそりできることを確認し、メソッドの半分を宣言します
protected
。しかし、私はテスト手順の弱点を利用しているので、捕まったときに自分自身を正当化することは別として、私はこれが好きではありません。また、それは応急措置です。必要な操作の数が2倍になった場合はどうなりますか?可能性は低いですが、それではどうでしょうか。 - このフェーズを任意にAnnealedObjectAlpha、AnnealedObjectBravo、AnnealedObjectCharlieに分割し、各ステージで3分の1の操作を実行できます。これは実際には複雑さを増す(N-1クラス増える)という印象の下にあり、テストに合格する以外に利点はありません。もちろん、CarWithFrontSeatsInstalledとCarWithAllSeatsInstalledは論理的に連続したステージであると私は考えることができます。後でAlphaがBravoメソッドを必要とするリスクは小さく、うまく使えばさらに小さくなります。それでも。
- リモートで類似したさまざまな操作を1つの操作にまとめることができます。
performAllSeatsInstallation()
。これはあくまで暫定的な措置であり、単一操作の複雑さが増します。操作AとBを異なる順序で実行する必要があり、E =(A + C)とF(B + D)内にそれらをパックした場合、EとFをアンバンドルしてコードをシャッフルする必要があります。 - ラムダ関数の配列を使用して、チェックを完全に回避できますが、それは不格好です。ただし、これはこれまでのところ最良の代替手段です。それは反射を取り除くでしょう。私が抱えている2つの問題は、仮説だけでなく、すべてのメソッドオブジェクトを書き換えるように求められることです。これは
CarWithEngineInstalled
非常に優れたジョブセキュリティですが、それほど魅力的ではありません。また、コードカバレッジチェッカーにはラムダに関する問題があります(これは解決可能ですが、まだ問題です)。
そう...
- あなたは、どちらが私の最良の選択肢だと思いますか?
- 私が考慮していないより良い方法はありますか?(たぶん、私はきれいに来て、それが何であるかを直接尋ねた方がいいですか?)
- この設計にはどうしようもない欠陥があり、私は敗北と溝掘りを認めるべきです-このアーキテクチャは完全にそうですか?私のキャリアには良くありませんが、長期的には、設計が不適切なコードを書く方が良いでしょうか?
- 私の現在の選択は実際にはOne True Wayであり、より良い品質のメトリック(および/またはインストルメンテーション)をインストールするために戦う必要がありますか?この最後のオプションについては、参照が必要です...つぶやいているときに@PHBで手を振るだけでは、これらは探しているメトリックではありません。いくらでもやりたい