インスタンスを複数のレイヤーに渡すのは悪い習慣ですか?


60

私のプログラム設計では、オブジェクトインスタンスをいくつかのクラスに渡さなければならないことがよくあります。たとえば、オーディオファイルを読み込んでプレーヤーに渡すコントローラーがあり、プレーヤーがそれをplayerRunnableに渡し、playerRunnableが別の場所などに再び渡す場合、見た目は悪くなりますが、それを避ける方法を知ってください。それともこれをしても大丈夫ですか?

編集:後でファイルを読み込むことができるため、プレーヤーの例が最適ではないかもしれませんが、それ以外の場合は動作しません。

回答:


54

他の人が述べたように、これは必ずしも悪い習慣ではありませんが、レイヤーの関心の分離を壊し、レイヤー固有のインスタンスをレイヤー間で受け渡さないことに注意する必要があります。例えば:

  • データベースオブジェクトを上位レイヤーに渡さないでください。DALでDataAdapterを使用してDTOまたはデータセットを作成し、それを渡すのではなく、.NETのDataAdapterクラス、DBアクセスクラスを使用し、UI層に渡すプログラムを見てきまし た。DBアクセスはDALのドメインです。
  • もちろん、UIオブジェクトはUIレイヤーに限定する必要があります。繰り返しになりますが、コンテンツの配列/ DTOの代わりにBLレイヤーに渡されたユーザーデータと、(私のお気に入りの)階層データを取得するDALクラスで埋められたListBoxesの両方で、この違反を見ましたDB。階層データ構造を返すのではなく、TreeViewオブジェクトを作成してデータを追加し、UIに返してフォームに動的に追加しました。

ただし、渡すインスタンスがDTOまたはエンティティ自体である場合は、おそらく大丈夫です。


1
これは衝撃的なように聞こえるかもしれませんが、.NETの暗い初期には一般的に推奨される方法であり、おそらく他のほとんどのスタックが実行しているものよりも優れていました。
ワイアットバーネット

1
同意しません。マイクロソフトは、WinformsクライアントがDBにもアクセスし、DataAdapterが非表示コントロールとしてフォームに直接追加されたシングルレイヤーアプリの実践を承認したことは事実ですが、それはOPのN層セットアップとは異なる特定のアーキテクチャです。しかし、多層アーキテクチャでは、これは.NET以前でもVB6 / DNAに当てはまり、DBオブジェクトはDB層にとどまりました。
アヴナーシャハル

明確にするために、「データレイヤー」からUI(例ではリストボックス)に直接アクセスする人々を見ましたか?私は..すごい..私は生産のコードに、このような違反行為に遭遇してきたとは思わない
サイモン・ホワイトヘッド

7
@SimonWhiteheadまさに。ListBoxと配列の区別について曖昧で、DTOとしてListBoxを使用した人々。他の人にとって直感的ではない、私がどれほど多くの目に見えない仮定をするのかを理解するのを助けた瞬間でした。
アヴナーシャハルカシュタン

1
@SimonWhitehead-はい、VB6およびVB.NET Framework 1.1および2.0のプログラムで、そのようなモンスターを維持するように任されていることを、残念ながら見ました。それは非常にgetsく、非常に速くなります。
-jfrankcarr

15

興味深いことに、不変オブジェクトについてはまだ誰も話していません。不変オブジェクトをさまざまなレイヤーすべてに渡すことは、各レイヤーに多数の短命オブジェクトを作成するよりも、実際には良いことだ思います。

Eric Lippertによる彼のブログには、不変性に関する素晴らしい議論がいくつかあります。

一方、レイヤー間で可変オブジェクトを渡すことは設計が悪いと主張します。基本的に、周囲のレイヤーがコードを壊すような方法でレイヤーを変更しないことを約束してレイヤーを構築しています。


13

オブジェクトインスタンスを渡すことは、通常のことです。状態(インスタンス変数)を保持する必要性を減らし、実行コンテキストからコードを分離します。

直面する可能性のある問題の1つは、チェーンの下部にあるメソッドのパラメーター要件の変更に応じて呼び出しチェーンに沿って複数のメソッドのシグネチャを変更する必要がある場合のリファクタリングです。ただし、リファクタリングを支援する最新のソフトウェア開発ツールを使用することで軽減できます。


6
私はあなたが直面するかもしれない別の問題は不変性を伴うと思います。開発者が特定のクラスだけがそのオブジェクトへの参照を持つものではないという事実を考えずにDTOを変更したプロジェクトで、非常に困惑したバグを覚えています。
フィル

8

マイナーかもしれませんが、この参照をいずれかのレイヤーのどこかに割り当てると、後で参照がぶら下がるか、メモリリークが発生する可能性があります。


あなたのポイントは正しいですが、OPの用語(「オブジェクトインスタンスの受け渡し」)から、私は彼が値(ポインタではなく)を渡しているか、ガベージコレクション環境(Java、C#、Python、Go 、. ..)。
モハマドデガン

7

コードのリモートエリアで必要なだけでオブジェクトを渡す場合、制御反転と依存性注入のデザインパターンをオプションの適切なIoCコンテナーと共に使用すると、オブジェクトインスタンスの持ち運びに関する問題をうまく解決できます。中規模のプロジェクトで使用したことがありますが、使用せずにサーバーコードの大部分を書くことはもう考えません。


それは面白いように聞こえます。私はすでにコンストラクター注入を使用しています。そして、高レベルのコンポーネントが低レベルのコンポーネントを制御していると思います。インスタンスの持ち運びを避けるために、IOCコンテナをどのように使用しますか?
パックル

私はここで答えを見つけたと思う:martinfowler.com/articles/injection.html
Puckl

1
Javaで作業している場合、Guiceは非常に優れており、バインディングを要求などにスコープできるため、その時点で高レベルコンポーネントがスコープを作成し、インスタンスを正しいクラスにバインドできます。
デイブ

4

レイヤーの束にデータを渡すことは悪いことではありません。レイヤードシステムがレイヤー構造に違反することなく機能する唯一の方法です。問題がある兆候は、目標を達成するために同じレイヤーの複数のオブジェクトにデータを渡すときです。


3

クイックアンサー:オブジェクトのインスタンスを渡すのに問題はありません。また言及したように、ポイントは、ぶら下がり参照またはメモリリークを引き起こす可能性のあるすべてのレイヤーでこの参照の割り当てをスキップすることです。

プロジェクトでは、このプラクティスを使用して、レイヤー間でDTO(データ転送オブジェクト)を渡します。これは非常に役立つプラクティスです。また、要約情報のように、より複雑な1回構築するためにdtoオブジェクトを再利用します。


3

私は主にWeb UIの開発者ですが、直感的な不快感はインスタンスのパススルーではなく、そのコントローラーで少し手続きを進めているという事実のほうが多いようです。コントローラーはこれらすべての詳細に汗をかく必要がありますか?オーディオを再生するために、他のオブジェクトの名前を複数参照するのはなぜですか?

OOP設計では、何が常緑で、何が変更される可能性が高いのかを考える傾向があります。変更対象は、大きなオブジェクトボックスに入れたい傾向があるため、プレーヤーが変更されたり新しいオプションが追加されたりしても、一貫したインターフェイスを維持できます。または、オーディオオブジェクトまたはその中のコンポーネントを大量に交換したい場合があります。

この場合、コントローラーは、オーディオファイルを再生する必要があることを識別し、それを再生するための一貫した/常緑の方法を持つ必要があります。一方、オーディオプレーヤーは、テクノロジーやプラットフォームが変更されたり、新しい選択肢が追加されたりすると、簡単に変更される可能性があります。これらの詳細はすべて、より大きな複合オブジェクトIMOのインターフェースの下に配置する必要があり、オーディオの再生方法の詳細が変更されたときにコントローラーを書き換える必要はありません。次に、ファイルの場所などの詳細を含むオブジェクトインスタンスを大きなオブジェクトに渡すと、適切なコンテキストの内部ですべてのスワッピングが行われ、誰かが愚かなことをする可能性は低くなります。

したがって、この場合、オブジェクトインスタンスが放り込まれているのはあなたを悩ませているとは思わない。キャプテンピカードはワープコアをオンにするためにエンジンルームに向かって走り、ブリッジに戻って座標をプロットし、シールドをオンにした後に「パンチイット」ボタンを押すだけです。 Warp 9の惑星Xに行きましょう。」そして彼の乗組員に詳細を整理させます。彼がそのように扱うとき、彼はすべての船のレイアウトとすべてがどのように機能するかを知らなくても、艦隊のどの船でもキャプテンすることができるからです。そして、それは最終的に、撮影する最大のOOPデザインの勝利、IMOです。


2

アプリがそのようなことに敏感な場合はレイテンシーの問題が発生する可能性がありますが、最終的には非常に一般的な設計です。


2

この問題は、動的スコープの変数(言語に変数がある場合)またはスレッドローカルストレージで解決できます。これらのメカニズムにより、いくつかのカスタム変数をアクティベーションチェーンまたは制御のスレッドに関連付けることができるため、これらの値を他のコードと通信できるようにするためだけに、これらの値を関係のないコードに渡す必要はありません。それらが必要です。


2

他の答えが指摘したように、これは本質的に貧弱な設計ではありません。ネストされたクラスとそれらをネストするクラスの間に密結合を作成できますが、参照のネストが設計に値を提供する場合、結合を緩めることは有効なオプションではない場合があります。

考えられる解決策の1つは、コントローラークラスのネストされた参照を「フラット化」することです。

ネストされたオブジェクトを介してパラメーターを数回渡す代わりに、すべてのネストされたオブジェクトへの参照をコントローラークラスで維持できます。

これがどの程度正確に実装されるか(または有効なソリューションであるかどうか)は、次のようなシステムの現在の設計に依存します。

  • コントローラーにネストされたオブジェクトのある種のマップを、複雑にならずに維持できますか?
  • パラメータを適切なネストされたオブジェクトに渡すとき、ネストされたオブジェクトはすぐにパラメータを認識できますか、それともネストされたオブジェクトにパラメータを渡すときに追加機能が発生しましたか?

これは、GXTクライアントのMVCデザインパターンで発生した問題です。GUIコンポーネントには、いくつかのレイヤーのネストされたGUIコンポーネントが含まれていました。モデルデータが更新されたとき、適切なコンポーネントに到達するまで、いくつかのレイヤーを通過しました。モデルデータを受け入れるための新しいGUIコンポーネントクラスが必要な場合、新しいクラスを含むすべてのGUIコンポーネントのモデルデータを更新するメソッドを作成する必要があるため、GUIコンポーネント間の望ましくない結合を作成しました。

それを修正するために、ViewクラスでネストされたすべてのGUIコンポーネントへの参照のマップを維持し、モデルデータが更新されるたびに、Viewが更新されたモデルデータを必要なGUIコンポーネントに直接送信できるようにしました。 。各GUIコンポーネントのインスタンスは1つしかなかったため、これはうまく機能しました。いくつかのGUIコンポーネントのインスタンスが複数ある場合、あまりうまく機能せず、どのコピーを更新する必要があるかを特定するのが難しくなります。


0

説明しているのは、Chain Of Responsibilityデザインパターンと呼ばれるものです。Appleは、イベント処理システムにこのパターンを使用しています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.