2010-11-22 14 views
3

値型のオブジェクトでは乱数ジェネレータを使用する必要がありますが、その依存関係の負担は必要ありません

私は現在、 Geneと呼ばれる不変型を持っています。これは2つのフィールドしか持っていません。

double value; 
Interval intervalOfAllowedValues; 

Geneスイッチをその範囲内にある限り他の値にランダムに切り替える必要があるintervalOfAllowedValues

私はこのために、INumberGenerator.GenerateDouble(...)を使って状況を管理する特別な方法

public Gene RandomMutation() { ... } 
//it returns a Gene because this class is immutable! 

を作成しました。問題は、RandomMutation()が引数としてIRandomNumberGeneratorを受け入れるか、またはGeneがコンストラクタ注入によって1つを取らなければならないことです。

RandomMutation()

どちらの解も好きではない:

RandomMutation()が引数で数値ジェネレータを受け入れるならば、GeneINumberGeneneratorについて知っているだけでなく、それを含むクラスも知っている必要があるということを意味する。

一方、コンストラクタインジェクションでINumberGeneratorをこのGeneに渡すと、ほとんどの場合、私はそれを使用せずにそこに持っていきます。私は、このGeneオブジェクトを値型として持つという目的を少し破ると感じています。 2つの異なるGeneが異なる数のジェネレータを持っているかどうかの微妙な問題も提起されています。はいの場合、それをユニットテストする方法は?

第3の選択肢があります:GeneクラスからRandomMutation()を離します。今の問題は、Geneを含むクラスがGeneIntervalの両方を知る必要があることです。これは望ましくないかもしれません。また、行動はそのデータの近くにあるべきであり、このアプローチに従うときにはそうではないだろう。

数値生成器をグローバル(シングルトン)にするというアプローチはまだ4番目(!)です。それは不思議に過ぎませんが、あらゆる依存関係を明示的にする哲学に反するでしょう。

どうやってこのような状況に対処しましたか?

ありがとう

答えて

1

INumberGeneratorのような依存関係が特定のGeneインスタンスの存続期間を通じてどのくらい利用されているかを評価するための有用な基準はありません。 の定義でが何回使用されているかを知ることが有用なのはGeneです。一度でも使用されているなら、ファーストクラスの依存関係のように扱われる価値があります。

大きな問題は、依存関係のあるstructがあることです。 RandomMutationは、機能するにはINumberGeneratorでなければなりませんが、コンストラクタまたはプロパティインジェクションのどちらを使用する場合でも、誰でもnew Gene()を呼び出してnull依存関係を持つインスタンスを作成できます。依存関係がstructに提供されていることを強制する方法はありません。

あなたにはいくつかのオプションがあります。最初にGeneclassと定義すると、コンストラクターの引数で必要な依存関係としてINumberGeneratorを宣言できます。これはオブジェクト指向の答えです。

第2の選択肢は、Genestructとし、機能を外部的に定義する、より機能的なアプローチです。機能プログラムは、動作とデータを結合しようとするOOとは対照的に、データ構造をそれらに作用するロジックから分離する傾向があります(LINQ参照)。あなたの問題は、このような方法でモデル化されることが最大の利益となるように思えます。

しかし、ランダムな突然変異を行うためにはGeneとの両方が必要であるという重大な問題が残ります。これはGeneの外にINumberGenerator依存性をとり、ときの遺伝子を知っているオブジェクトにバブルアップ

public static Gene Mutate(this INumberGenerator generator, Gene gene) 
{ 
    // ... 
} 

:我々はGeneは、おそらく程度INumberGenerator直接知っているべきではないと判断しているので、あなたは拡張メソッドとしてそれをモデル化することができます変異させるべきである。

2

テスト可能性があなたの目標であるならば、私はあなたの番目のオプションが好き。データの近くで行動することは、私の考えでは第2の目標ですが、テスト容易性とSRPは主要な目標です。

私の2番目の選択肢は、Geneにサービスを入れるためにコンストラクタインジェクションを使うことです(私はファンではありませんが、その点についてはセッター注入)。あなたの主な反論は、「私はそれを一切使わずにそこに置くことが大部分です。それはいいとは思われません」。これは、メソッドを直接クラスに追加することにも当てはまります。違いは、その機能を注入されたクラスに単純に移動していることです。

3

私は、テスト容易性だけでなく、一般的な設計上の考慮事項のために、あなたの第3の選択肢が確実に行く方法だと思います。遺伝子は最終的にデータを転送する方法ですが、それ自体の実際の動作はありません。突然変異は、単にそのデータに起こるものです。このように、RandomMutationは遺伝子に作用する能力を持っている他の場所に生きることが理にかなっています。

テスト容易性の面では、必要に応じてすべてを明示的に簡単に置き換えることができる最もクリーンな方法です。

3

Geneを受け入れ、変異遺伝子を返すmutateメソッドを持つGeneMutatorクラスを持つことに何も問題はありません(INumberGeneratorへの参照を保持します)。

Intervalはかなり一般的なようですが、私はそれがGeneの外側に見えることに欠点は見当たりません。

関連する問題