4

これが私のプログラムです:

class Program
{
    //DESIGN 1
    abstract class AFoo
    {
        public string Bar { get; set; }
        public abstract string SayHi();
    }

    class LoudFoo : AFoo
    {
        public override string SayHi()
        {
            return this.Bar.ToUpper();
        }
    }

    class QuietFoo : AFoo
    {
        public override string SayHi() { return this.Bar.ToLower(); }
    }

    //DESIGN 2
    class Foo{
        public string Bar { get; set; }
        public Func<Foo, string> SayHi { get; set; }
    }

    static void Main(string[] args)
    {
        //USING DESIGN 1
        var quietFoo2 = new QuietFoo{ Bar = "Mariane"};

        var loudFoo2 = new LoudFoo{ Bar = "Ginger"};

        Console.WriteLine(quietFoo2.SayHi());
        Console.WriteLine(loudFoo2.SayHi());

        //USING DESIGN 2
        var quietFoo = new Foo
        {
            Bar = "Felix",
            SayHi = (f) => { return f.Bar.ToLower(); }
        };
        var loudFoo = new Foo
        {
            Bar = "Oscar",
            SayHi = (f) => { return f.Bar.ToUpper(); }
        };
        Console.WriteLine(quietFoo.SayHi(quietFoo));
        Console.WriteLine(loudFoo.SayHi(loudFoo));

    }
}

私は「同じこと」を達成できます。実際にはまったく同じことではありませんが、似たようなことを 2 つの異なるルートで行うことができます。

設計 1)そのクラスの実装者に SayHi() の方法を強制する抽象クラスを作成できます

- また -

設計 2)関数である SayHi プロパティを定義するクラスを作成できます。(私はそれをデリゲートと呼んでいますが、ここでの正しい用語かどうかはわかりません)

設計 1 は、クラスの急増につながる可能性があるため、気になります

まだ....

設計 2 は、Foo を実際に SayHi() にする必要がある場合に冗長に感じられるため、気になります。

felix.SayHi(felix)

私の質問は、デザイン 1 とデザイン 2 のどちらを使用する方が良いか、あるいはどちらも使用しない方が良いかということです。私がより良いと言うとき、私は自分のプログラムを維持できるという点でどちらがより実用的であるかを言っています. さまざまなクラウド API (Google Drive、Box.com、DropBox) からファイルをダウンロードするために使用されるさまざまなクラスを作成したときに、これに遭遇しました。最初は別々のクラスを作成しましたが、別のルートに行きました。

4

6 に答える 6

5

これらのタイプの設計の選択に関しては、モデル化しようとしている問題領域の観点からオブジェクトについて考えることが役立つことがわかりました。LoudFoo と QuietFoo が 1 つの動作で異なることを示しましたが、これは意図的に単純化された例です。実際のシステムでは、2 つのオブジェクトを概念的に別個のものとみなす説得力のある理由がある場合があります。

以前のバージョンでは、SayHi はクラス動作の本質的な部分であり、その動作の性質が何らかの方法でその内部状態と相互作用する場合に適しています。おそらく、SayHi の実装は、その派生クラスの型に固有のオブジェクトのプロパティに依存します。

後者のバージョンでは、SayingHi はさまざまなインスタンスに配布できるツールに似ています。これは、異なるタイプの Foo インスタンスを区別する理由が他にない場合に適しています。

Streamは前者のパターンの良い例です。Stream が提供するさまざまなメソッドは、ストリーミング操作の性質に固有のものです。さまざまな派生クラスは、さまざまな状態を利用してメソッドを実装します。

Comparerは後者のパターンの良い例です。多くの異なるオブジェクト タイプが比較の概念を使用して操作する必要があります。この機能を使用するクラスは、この特定の種類の動作を使用すること以外に共通点を持つ必要はありません。


この質問のきっかけとなった具体的なアプリケーションについてですが、マルチクラスのアプローチについてはどうでしたか? 冗長性が忍び寄っている場合は、問題をより適切にモデル化する別の方法で責任を考慮できることを示している可能性があります。特定の問題に関する追加の詳細を知らずにこれ以上言うのは難しいですが、おそらく良いアプローチは、あなたが提案した2つの組み合わせであり、操作の順序付けを担当する単一のクラスと、別の階層(またはインターフェース実装のセット)です。 ) 各サービスに固有の操作を実装します。基本的に、インターフェイス (または基本クラス) は、個別に渡すさまざまなデリゲートをすべてグループ化します。これは、StreamReader が Stream を取得し、Stream で動作する追加の動作で強化する方法に似ています。

于 2013-06-22T14:10:53.130 に答える
0

設計 2 は、実際に Foo を SayHi() にする必要がある場合に冗長に感じられるため、気になります。

Fooクラスがに再定義された場合

class Foo
    public property Bar as string
    public property SayHi as func(of string)
end class

その後、クロージャーを使用して、関数パラメーターとしてSayHi渡すことなく関数を作成および呼び出すことができます。Foo

dim Bar = "Felix"

dim Felix as new Foo with {
    .Bar = Bar,
    .SayHi = function() Bar.toLower
}

dim FelixSays = Felix.SayHi()

動作の実装がクラス内でブラックボックス化されているため、私はデザイン 1 に傾いています。

設計 2 は、たとえばファクトリ メソッド内など、常にブラック ボックス動作の実装の準備ができています。

function CreateQuietFoo(Bar as string) as Foo
    return new Foo with {
        .Bar = Bar,
        .SayHi = function() Bar.toLower
    }
end function

dim Felix = CreateQuietFoo("Felix")
dim Oscar = CreateQuietFoo("Oscar")

このように、呼び出し元はSayHi、quietFooインスタンスを作成するメソッドを提供する必要がなく、CreateQuietFooファクトリ メソッドを使用するだけです。

私の質問は、設計 1 と設計 2 のどちらを使用する方がよいか、あるいはどちらも使用しない方がよいかということです。

Inheritance よりも合成を優先する場合は、デザイン 2 を使用してください。コードをより柔軟にします。

于 2015-09-14T11:05:04.083 に答える