メソッドチェーンを介してコンテキストを渡すためのパターン


19

これは、非常に多くのように思われる設計上の決定です。コンテキスト必要としないメソッドにコンテキストを渡す方法にコンテキストを渡す方法。正しい答えがありますか、それともコンテキストに依存しますか。

ソリューションを必要とするサンプルコード

// needs the dependency
function baz(session) {
  session('baz');
}

// doesn't care about the dependency
function bar() {
  baz();
}

// needs the dependency
function foo(session) {
   session('foo')
   bar();
}

// creates the dependency
function start() {
  let session = new Session();
  foo(session);
}

可能な解決策

  • スレッドローカル
  • グローバル
  • コンテキストオブジェクト
  • 依存関係を通過させる
  • bazをカレーし、依存関係を最初の引数としてbarに渡します
  • 依存性注入

どこにあるのかの例

HTTPリクエスト処理

要求属性の形式のコンテキストオブジェクトがよく使用されます。expressjs、Javaサーブレット、または.netのowinを参照してください。

ロギング

Javaロギングでは、多くの場合、グローバル/シングルトンを使用します。典型的なlog4j / commons logging / javaのロギングパターンを参照してください。

取引

スレッドローカルは、メソッド呼び出しのチェーンに関連付けられたトランザクションまたはセッションを保持するためによく使用され、それらを必要としないすべてのメソッドにパラメーターとして渡す必要がなくなります。


より意味のある例を使用してください。
Tulainsコルドバ

私はそれがどこに来るのかのいくつかの例を追加しました。
ジェイミーマクリンドル

3
もっと意味のあるサンプルコードを意味しました。
Tulainsコルドバ

回答:


11

唯一の公正な答えは、プログラミングパラダイムのイディオムに依存するということです。オブジェクト指向を使用している場合、メソッドからメソッドへの依存関係を渡すことはほぼ間違いなく間違っています。それはオブジェクト指向のコード臭です。実際、それはオブジェクト指向が解決する問題の1つです。オブジェクトがコンテキストを修正します。したがって、オブジェクト指向では、1つの正しい(常に他の方法があります)アプローチは、コンストラクターまたはプロパティを介して依存関係を提供することです。コメント者は「依存性注入」に言及しており、それは完全に合法ですが、厳密に必要というわけではありません。fooおよびのメンバーとして利用できるように、依存関係を提供するだけbazです。

カリー化について言及しているので、関数型プログラミングは問題外ではないと思います。その場合、オブジェクトコンテキストの哲学的な同等物はクロージャです。ここでも、依存関係を修正して依存関係で使用できるようにするアプローチは、問題なく機能します。カレーはそのようなアプローチの1つです(そして、それはあなたを賢く聞こえさせます)。依存関係を閉じる他の方法があることを覚えておいてください。それらのいくつかはエレガントで、いくつかはひどいです。

アスペクト指向プログラミングを忘れないでください。過去数年で好評を失ったようですが、その主な目標は、あなたが説明した問題を正確に解決することです。実際、古典的なアスペクトの例はロギングです。AOPでは、他のコードが記述された後に依存関係が自動的に追加されます。AOPの人々はこれを「織り」と呼びます。共通の側面は、適切な場所でコードに織り込まれています。これにより、コードの考えが簡単になり、かなりクールになりますが、新しいテストの負担も増えます。最終的なアーティファクトが適切かどうかを判断する方法が必要です。AOPにもその答えがありますので、怖がらないでください。


OOメソッド内でパラメーターを渡すことはコードの匂いだと主張することは、非常に議論の余地のあるステートメントです。私は完全に反対だと主張します:クラス内での状態と機能の混合を奨励することは、オブジェクト指向パラダイムによって引き起こされる最大の間違いの1つであり、コンストラクターではなくメソッドに直接依存関係を注入することで回避することは、うまく設計されたピースの兆候ですオブジェクト指向かどうかに関係なく、コードの。
デビッドアルノ

3
@DavidArnoオブジェクトステートフルネスと異なるパラダイムを使用することは、「OOパラダイムによる最大の誤りの1つ」であり、パラダイムを回避することをお勧めします。私はほとんどどんなアプローチに対しても何もしませんが、一般的に作者が彼らのツールと戦っているコードを嫌います。プライベート状態は、OOの際立った機能です。その機能を避けると、OOのパワーの一部が失われます。
スキャントロジャー

1
@DavidArnoすべて状態であり、機能を持たないクラスには、状態で不変の関係を強制するメカニズムがありません。このようなクラスはまったくオブジェクト指向ではありません。
ケビンクルムウィーデ

@KevinKrumwiede、ある程度まで、私のコメントにreducto ad absudiumを適用しましたが、あなたの主張はまだよくできています。状態の不変性は、「オブジェクト指向からの移行」の重要な部分です。したがって、機能と状態の混合を避けるには、不変(コンストラクターによって設定され、ゲッターを介してアクセスされるカプセル化されたフィールド)を実現するために、必要に応じて状態オブジェクトに十分な機能を許可する必要があります。
デビッドアルノ

@ScantRoger、私は別のパラダイム、すなわち機能的パラダイムを採用できることに同意します。興味深いことに、現代のほとんどの「OO」言語には機能的な機能のリストが増えているため、「ツールと戦う」ことなく、それらの言語に固執して機能パラダイムを採用することが可能です。
デビッドアルノ

10

barがに依存している場合baz、それが順番に必要となりdependency、正しく使用するためにもbar必要です。したがって、適切なアプローチは、依存関係をパラメーターとしてに渡すか、カレーにしてに渡すことです。dependencybazbarbazbar

最初のアプローチは実装と読み取りがより簡単ですが、barとの間に結合を作成しますbaz。2番目のアプローチは、その結合を除去しますが、コードが不明瞭になる可能性があります。したがって、どちらのアプローチが最適かは、両方の機能の複雑さと動作に依存する可能性があります。たとえば、副作用があっbazたり、dependency副作用がある場合、テストの容易さはソリューションが選択される大きな要因になります。

あなたが提案する他のすべてのオプションは、本質的に「ハッキング」であり、テストとバグの追跡が難しい問題の両方につながる可能性が高いことをお勧めします。


1
私はほぼ完全に同意します。依存性注入は、別の非「ハッキング」アプローチかもしれません。
ジョナサンヴァンデヴィーン

1
@JonathanvandeVeen、確かにdependencyパラメーターを介して渡す行為は依存性注入ですか?
デビッドアルノ

2
@DavidArno依存性注入フレームワークは、これらの種類の依存関係を取り除くのではなく、それらを移動するだけです。魔法は、彼らがあなたのクラスの外に、テストが誰か他の人の問題になる場所にそれらを移動することです。
ケビンクルムウィーデ

@JonathanvandeVeen私は同意します、依存性注入は有効な解決策です。実際、私が最も頻繁に選ぶものです。
ジェイミーマクリンドル

1

哲学的に話す

David Arnoの懸念に同意します。

私は実装ソリューションを探しているとしてOPを読んでいます。ただし、答えはデザインを変更することです。「パターン」?オブジェクト指向の設計は、コンテキストに関するものだと言えます。それは可能性を秘めた広大な空白の紙です。

既存のコードを扱うことは、異なる、まあ、コンテキストです。



私は現在、まさに同じ問題に取り組んでいます。さて、私は値を注入できるように行われた数百行のコードのコピーアンドペーストを修正しています。

コードをモジュール化する

600行の重複コードを破棄してリファクタリングしたので、「A呼び出しB呼び出しC呼び出しD ...」の代わりに「呼び出しA、戻り、呼び出しB、戻り、呼び出しC ...」があります。ここで、これらのメソッドの1つに値を注入するだけで済みます。メソッドEとしましょう。

コンストラクターにデフォルトのパラメーターを追加します。既存の発信者は変更されません-ここでは「オプション」が有効な単語です。引数が渡されない場合、デフォルト値が使用されます。次に、リファクタリングされたモジュール構造に変数を渡すために1行だけ変更します。そして、それを使用する方法Eの小さな変更。


閉鎖

プログラマースレッド-「なぜプログラムはクロージャーを使用するのですか?」

基本的に、値でカスタマイズされたメソッドを返すメソッドに値を注入しています。その後、そのカスタマイズされたメソッドが実行されます。

この手法により、シグネチャを変更せずに既存のメソッドを変更できます。


このアプローチは見え妙に馴染みの ...

一時的なカップリング(リンク)の問題に関するロジャー、@ Snowman。必要な実行順序をカプセル化することが重要です。
レーダーボブ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.