3

緩い結合とインターフェースについて、概念的/理論的な質問があります。

したがって、インターフェースを使用する1つの方法は、特定のコンストラクターに必要なパラメーターをカプセル化することです。

class Foo
{
    public Foo(IFooInterface foo)
    {
       // do stuff that depends on the members of IFooInterface
    }
}

渡されたオブジェクトがコントラクトを実装している限り、すべてが機能します。私の理解では、ここでの主な利点はポリモーフィズムを可能にすることですが、これが本当に緩い結合と関係があるかどうかはわかりません。

議論のために、IFooInterfaceは次のようになっているとしましょう。

interface IFooInterface
{
   string FooString { get; set; }
   int FooInt { get; set; }

   void DoFoo(string _str); 
}

緩い結合の観点からは、上記のコンストラクターでIFooInterfaceを使用せず、代わりにFooを次のように設定する方がはるかに良いでしょう。

class Foo
{
    public Foo(string _fooString, int _fooInt, Action<string> _doFoo)
    {
       // do the same operations
    }
}

Fooの機能を別のプロジェクトに落としたいと言うからです。つまり、他のプロジェクトもIFooInterfaceを参照し、別の依存関係を追加する必要があります。しかし、この方法でFooを別のプロジェクトにドロップすることができ、それが機能するために必要なものを正確に表現します。明らかに、オーバーロードされたコンストラクターを使用することはできますが、議論のために、Fooのコンストラクターを変更したくない、および/または変更できないとしましょう。

(少なくとも私にとって)最も顕著な欠点は、多数のプリミティブパラメータを持つメソッドがあると、醜くて読みにくくなることです。そこで、すべてのプリミティブ型ではなく、インターフェイスを渡すことができる一種のラッピング関数を作成するというアイデアがありました。

    public static Func<T, R> Wrap<T, R>(Func<T, object[]> conversion)
    {
        return new Func<T, R>(t =>
        {
            object[] parameters = conversion(t);

            Type[] args = new Type[parameters.Length];

            for (int i = 0; i < parameters.Length; i++)
            {
                args[i] = parameters[i].GetType();
            }

            ConstructorInfo info = typeof(R).GetConstructor(args);

            return (R)info.Invoke(parameters);
        });
    }

ここでの考え方は、Fooの要件に準拠するインターフェイスのインスタンスを取得する関数を取り戻すことができるということですが、Fooは文字通りそのインターフェイスについて何も知りません。次のように使用できます。

public Foo MakeFoo(IFooInterface foo)
{
    return Wrap<IFooInterface, Foo>(f => 
       new object[] { f.FooString, f.FooInt, f.DoFoo })(foo);  
}

インターフェイスがどのようにルーズカップリングを可能にするかについての議論を聞いたことがありますが、これについて疑問に思っていました。

一部の経験豊富なプログラマーがどう思うか疑問に思います。

4

2 に答える 2

3

最初の例では、パラメーター オブジェクトパターンにかなり近いですが、インターフェイスの追加の抽象化なしで、ここでは単純なクラス (多くの場合、自動プロパティを使用) を使用する方が一般的です。

通常、インターフェースをコンストラクターに渡すことについて聞くとき、それはプリミティブを置き換えるためではなく、依存関係の注入の形式としてです。直接依存する代わりに、特定の実装への結合を削除するMyFooRepository依存関係を取ります。IFooRepository

于 2011-06-24T01:25:36.927 に答える
2

私の最初の考えは、とのセッターにそれぞれ と をAction<string>提供しなかったということです。の実装には、これらのセッターに関するルールがある場合があり、インターフェイスで公開されていない他の実装の詳細へのアクセスが必要になる場合があります。Action<int>FooStringFooIntIFooInterface

Func<string>同じように、 andも受け入れる必要がありFunc<int>ます。 の実装には、時間の経過に伴い、何と何を表すIFooInterfaceかについての規則がある場合があります。たとえば、これらの値を再計算する場合があります。それらが変更されないフィールドへの単なるパススルーであると想定することはできません。FooStringFooIntDoFoo

これをさらに進めると、ゲッター、セッター、またはDoFoo共通の状態へのアクセスが必要な場合、関数とアクションは、それらを作成するときに同じ変数のセットを閉じる必要があります。その時点で、変数の有効期間とデリゲート間の関係を理解するために、頭の体操を行います。

この状態と動作の組み合わせは、まさにクラスが表現するものであり、実装の詳細を隠すことは、まさにインターフェイスが提供するものです。これらの概念を構成要素に分割することは確かに達成可能ですが、メンバーをタイプでグループ化することによって得られる一貫性も破られます。

別の言い方をすれば、麺、ソース、野菜、ハンバーガーをくれますが、それはスパゲッティとミートボールではありません :-)

于 2011-06-24T01:32:14.067 に答える