1

私のプログラムには 2 つのクラスがあります。どちらも同じ基本クラスから派生します。

class A : MyBase
{
    internal A(InitVal initVal)
}

class B : MyBase
{
    internal B(InitVal initVal)
}

InitValコンストラクターを介して注入される別のクラスです。このクラスは内部使用用です。内部コンストラクターのため、ユーザーはクラスのインスタンスを直接作成できませAB。代わりに、これらのオブジェクトを作成するメソッドを作成しました。

class Initiator
{
    InitVal initVal;

    public T CreateObject<T>(ObjectInstance objectInstance) where T : MyBase
    {
        MyBase myBase = null;
        switch(objectInstance)
        {
            case ObjectInstance.A:
                myBase = new A(initVal);
                break;
            case ObjectInstance.B:
                myBase = new B(initVal);
                break;
        }
        return (T)myBase;
    }
    ...
}

ObjectInstance上記のコードの列挙型です。

これは問題なく動作しますが、これほど醜いコードを見たことがないはずです。

使用すべき作成パターンを提案してください。ObjectInstance機能を変更せずに列挙型を削除したい。大掃除になります。

dotfactoryに記載されているCreational Patternsを試しました。 この場合、適切に見えません。Factory MethodAbstract Factory

私のコードは醜く見えますが、読んで理解するのはとても簡単です。コードの複雑さを増す上記のパターンを実装してみました。したがって、これは回答を選択する際の私の基準でもあります。

Initiatorクラス以外のコードを変更することはできません。他のすべてのクラスには、編集のためにアクセスできません。

編集 1: 上記のコードが私の見解では醜い理由

1) メソッドの呼び出し中CreateObjectに、ユーザーはオブジェクトのタイプを 2 回指定する必要があります。

A a = initiator.CreateObject<A>(ObjectInstance.A);

最初はTジェネリック値、2 番目は列挙値です。これは避けたい。

2) ユーザーはオブジェクトのタイプを 2 回指定する必要があるため、間違える可能性があります。

A a = initiator.CreateObject<A>(ObjectInstance.B);

上記のコードでは、列挙値とジェネリック値が異なります。これは許可されておらず、問題になります。私のコードでは、これを避けることはできません。

それが理由です; 複雑さを増すことなく、自分のケースに合ったパターンを探しています。

どうにかして enum の必要性を取り除けば、コードはずっと良くなります。の署名を次のように変更できればCreateObject、ずっと良くなります。

public T CreateObject<T>() where T : MyBase

しかし、このメソッドを実装して適切なインスタンスを作成する方法がわかりません。

4

4 に答える 4

2

これをジェネリックにしようとすることで何らかの利点を得ているようには見えません。呼び出しサイトで返される値の具体的な型を知る必要があります。

したがって、物事をシンプルに保ち、これを実行してみませんか?

public class Initiator
{
    InitVal initVal;

    public A CreateA()
    {
        return new A(initVal);
    }

    public B CreateB()
    {
        return new B(initVal);
    }
}
于 2016-09-13T08:25:07.780 に答える
1

メソッドを一般的なものとして指定したので、コンパイル時にすでに取得したい型を実際に知っている可能性があると思います..だから私は次のようなものに行きます:

class Initiator
{ 
    public T CreateObject<T>(ObjectInstance objectInstance) where T : MyBase, new()
    {
        T newInstance = new T();
        newInstance.Value = initVal;

        return newInstance;
    }
...
}

次のように呼び出すことができます。

A myAInstance = initiator.CreateObject<A>();
MyBase myAInstance = initiator.CreateObject<A>();   //this also works

それを機能させるには、クラスで内部パラメーターなしのコンストラクターを指定し、Value プロパティのインターフェイスを指定するか、現在のコンストラクターで設定するものを指定する必要があります。

class MyBase{
    InitVal Value { get; set;}       //this allows construction of the object with parameterless constructor
    ...
}

これは、新しいタイプが追加されるたびに列挙型とメソッド本体の両方を編集する必要がないため、よりクリーンで短いだけでなく、エラーが発生しにくくなります。ただし、子タイプ固有のロジックの柔軟性は低くなります。

注: 現在のようにパラメーターを持つコンストラクターが本当に必要な場合は、このアプローチを使用できますが、リフレクション(Activator をチェック) またはラムダを使用する必要があります。

もちろん、これは、コンパイル時にタイプを決定できる場合、またはこの決定をサードパーティのライブラリに委譲したい場合にのみ意味があります。

switch(chosenType){
case ObjectInstance.A:
    instance = initiator.CreateObject<A>();
    ...

それ以外の場合は、そのままにしておきます。これは多かれ少なかれ FactoryMethod パターンであり、ジョブを実行します。その中のジェネリックパラメータだけが...その場合はまったく役に立たないようです。とにかくユーザーが T を指定できないため、それを削除して戻り値の型を MyBase に変更します。

最後のオプションは、type ごとに個別のメソッドを作成することです。これはクリーンで柔軟で、カスタマイズのための多くのオプションを提供しますが、多くの共有ロジックを繰り返す必要があり、それぞれに新しいものを追加する必要がある場合は最悪です次のタイプ。単に:

A CreateObjectA(InitVal initValue){
     return new A(initValue);
}
B CreateObjectB(InitVal initValue){ ...
于 2016-09-13T08:03:28.097 に答える