web-dev-qa-db-ja.com

数十のクラスとインターフェイスで使用されるメソッドに引数を追加するときに、数十のクラスとインターフェイスを変更しないようにするにはどうすればよいですか?

解決策がわからない以下の設計上の問題に何度か直面しました。たとえば、ある時点でAPIからJSONオブジェクトを受け取るアプリケーションを想像してみてください。このオブジェクトを使用する前に、アプリケーションはいくつかの変換を実行する必要があります。たとえば、1つの変換が日付をある形式から別の形式に変換し、別の変換がキャッシュからユーザーに説明するエンティティなどの追加のデータをキャッシュに追加する場合があります。元のオブジェクトの唯一のユーザーID)。

実行時に適用する変換を選択できるようにするために、変換はDateFormatTransformExtraDataFromCacheTransformなどの特定のクラスで定義されます。

基本的に、変換を実行するコードは次のようになります。

foreach (var transform in this.GetRelevantTransforms())
{
    entity = transform.Apply(entity);
}

this.GetRelevantTransformsは、DateFormatTransformExtraDataFromCacheTransform、およびその他のクラスのインスタンスを返します。これらの各クラスは、メソッドを含むインターフェースを実装します。

SampleEntity Apply(SampleEntity x)

後で、Applyの署名を変更する必要がある追加のニーズが発生する可能性があります。たとえば、現在のユーザーを認識するために変換が必要になる場合があります。ユーザーによっては、一部の変換は別の方法で実行される可能性があるためです。

SampleEntity Apply(SampleEntity x, User currentUser)

その間、私は数十の変換を行うことができ、そのような基本的な変更では、数十のファイルをウォークスルーして、インターフェイス(IDateFormatTransformなど)とクラス(DateFormatTransformなど)の両方を変更する必要があります。

単純な引数を1つ追加するだけで約50個のファイルを変更しなければならないのは、コードベースがクリーンであることを示すものではないと思います。

代替案は何ですか?

TransformArgumentsメソッドなどの常に唯一のパラメーターであるApplyなどのDTOを使用する必要がありますか?それとも他のテクニックはありますか?

または、一連の変換を使用した私のアプローチには最初から問題があります。そうであれば、どうすれば修正できますか?

6

基本的には、変換オブジェクトにパラメーターをカプセル化してポリモーフィズムに依存することにより、パラメーターを実際の呼び出しから分離する方法で、呼び出しサイトに有用な一般化されたインターフェースを思い付くことができるかどうかということになります。 。作成時にパラメーターを決定できる場合は、これを行うことができますが、コードの再考と再編成が必要になる場合があります(ただし、それは多かれ少なかれ避けられません)。

したがって、変更する代わりに
entity = transform.Apply(entity);

entity = transform.Apply(entity, user);
コードベース全体のどこでも、次のようにします。

_var transform = new UserBasedTransform(user);   // at creation site   
//--------------
entity = transform.Apply(entity);    // at call site
_

または、その時点でパラメータを特定できない場合は、後で可能になったときにパラメータを返すことができるファクトリを渡すことができます。
Transform transform = new UserBasedTransform(userFactory); // at creation site

ある意味で、変換のインターフェースは抽象化であり(たとえクラスのパブリックメソッドであり、実際のインターフェースタイプではない場合でも)、変換を操作する一般的な方法を表しています。コードベースの進化に伴って比較的安定している問題の側面に基づいて抽象化を行う必要があります。あなたの場合、変化し続けるのはパラメータなので、それらを実装の詳細として扱い、クライアントコード内のどこにも参照しないでください。安定しているように見えるのは、エンティティに変換を適用しているため、クライアント向けのインターフェースをその周りに編成することです(そうでない場合は、そのプロパティを持つものを見つけて使用します)。これは観察した変更パターンに固有のものであることに注意してください。すべての状況に適合する1つのソリューションを見つけることではなく、特定のドメインにうまく機能するデザインを見つけることです。

これによりクライアントが簡略化される可能性がありますが、変換オブジェクトを構築するには別のアプローチが必要になる場合があります。

7

トランスフォーマがユーザーの知識を必要とするかどうかは実装の詳細であり、呼び出し元には関係がないようです

これを前提として、それを必要とするトランスフォーマーにユーザーコンテキストを注入できますか?

class UserTransform {

    public UserTransform(IUserContextAccessor userContextAccessor) {
        _userContextAccessor = userContextAccessor
    }

    public SampleEntity Apply(SampleEntity x) {
        var user = _userContextAccessor.CurrentUser;
        // do user specific thing
    }
}
1
JamesT