7

作成しようとしているDTOマッパーにファクトリパターンを実装する方法を理解するのに苦労しています。私は自分のデザインを再考する必要があると確信しています。これが私が実行しているものの非常に小さな例です:

    public abstract class Person
{
    public string Name { get; set; }
    public decimal Salary { get; set; }
}

public class Employee : Person
{
    public Employee()
    {
        this.Salary = 20000;
    }

}

public class Pilot : Person
{
    public string PilotNumber { get; set; }

    public Pilot()
    {
        this.Salary = 50000;
    }
}

public static class PersonFactory
{
    public static Person CreatePerson(string typeOfPerson)
    {
        switch (typeOfPerson)
        {
            case "Employee":
                return new Employee();
            case "Pilot":
                return new Pilot();
            default:
                return new Employee();
        }
    }
}

と工場を使用するには:

Person thePilot = PersonFactory.CreatePerson("Pilot");
        ((Pilot)thePilot).PilotNumber = "123ABC";

パイロット番号をパイロットに型キャストせずにロードする方法を教えてください。これはこれを行うための間違った方法ですか?パイロット番号をPersonクラスに入れることはできますが、その場合、Employeeは番号を継承するため、それは私が望んでいることではありません。私に何ができる?

ありがとう!

-ジャクソン

4

4 に答える 4

14

ファクトリパターンは、オブジェクトの実装がインターフェイスではなく異なる場合に最適に使用されます。あなたの場合、ファクトリパターンはあまり有益ではなく、オブジェクトを直接作成する方がおそらく良いでしょう(または他のパターンがおそらくより良いでしょう)。

于 2010-08-26T19:22:56.017 に答える
10

特定のタイプのメソッドをPersonFactoryクラスに追加したり、ジェネリックCreatePerson<T>()メソッドを追加したりできますが、これは、呼び出し元が受信する必要のある人のタイプをすでに知っている場合にのみ役立ちます。多分これは事実であるか、そうでないかもしれません。

このシナリオでは、PersonFactory.CreatePersonを実際に呼び出しているコードは、返される人の種類を認識または気にしないと予想されます。その時点以降に、自分が持っている人物オブジェクトのタイプをすでに知っているか把握しているコードがある場合は、それをキャストする必要があります。

以下は、ファクトリとさまざまな使用シナリオで何ができるかを示すコード例であり、単にキャストする必要がある場合とそうでない場合を説明しようとしています。

public static class PersonFactory
{
    public static Person CreatePerson()
    {
        return new Person();
    }

    public static Employee CreateEmployee()
    {
        return new Employee();
    }

    public static Pilot CreatePilot()
    {
        return new Pilot();
    }

    public static T CreatePerson<T>()
        where T : Person
    {
        return (T)CreatePerson(typeof(T));
    }

    public static Person CreatePerson(Type type)
    {
        if (type == typeof(Person))
            return CreatePerson();
        else if (type == typeof(Employee))
            return CreateEmployee();
        else if (type == typeof(Pilot))
            return CreatePilot();
        else
            throw new ArgumentOutOfRangeException(string.Format(CultureInfo.InvariantCulture, "Unrecognized type [{0}]", type.FullName), "type");
    }

    public static Person CreatePerson(string typeOfPerson)
    {
        switch (typeOfPerson)
        {
            case "Employee":
                return CreateEmployee();
            case "Pilot":
                return CreatePilot();
            default:
                return CreateEmployee();
        }
    }
}



class UsageExample
{
    Person GetPerson()
    {
        Pilot p;
        p = (Pilot)PersonFactory.CreatePerson("Pilot"); // this code already knows to expect a Pilot, so why not just call CreatePilot or CreatePerson<Pilot>()?
        p = PersonFactory.CreatePilot();
        p = PersonFactory.CreatePerson<Pilot>();
        return p;
    }

    Person GetPerson(Type personType)
    {
        Person p = PersonFactory.CreatePerson(personType);
        // this code can't know what type of person was just created, because it depends on the parameter
        return p;
    }

    void KnowledgableCaller()
    {
        Type personType = typeof(Pilot);

        Person p = this.GetPerson(typeof(Pilot));
        // this code knows that the Person object just returned should be of type Pilot

        Pilot pilot = (Pilot)p;
        // proceed with accessing Pilot-specific functionality
    }

    void IgnorantCaller()
    {
        Person p = this.GetPerson();
        // this caller doesn't know what type of Person object was just returned

        // but it can perform tests to figure it out
        Pilot pilot = p as Pilot;
        if (pilot != null)
        {
            // proceed with accessing Pilot-specific functionality
        }
    }
}
于 2010-08-26T19:27:31.803 に答える
1

必要なタイプを伝えるために文字列を使用する必要がありますか?代わりにジェネリックを使用できます。

public static T CreatePerson<T>() where T : Person

CreatePerson内で実際に行っていることの詳細がわからないため、これが機能するかどうかを正確に判断するのは困難です。パラメータなしのコンストラクタを呼び出すだけの場合、それは簡単です。

public static T CreatePerson<T>() where T : Person, new()
{
    return new T();
}

ただし、発信者が代わりにそれを行うことができるので、それも合理的に無意味です。ジェネリックはあなたの実際の状況で実行可能ですか?そうでない場合は、その理由を説明していただければ、回避策を講じることができますか?

于 2010-08-26T19:22:59.683 に答える
1

これを回避する簡単な方法はありません。

PilotNumberプロパティを使用するには、Pilotタイプが必要です。ファクトリパターンを使用するということは、Personのさまざまなサブタイプを放棄することを意味します。

それが慰めであるならば、BCLは同様のパターンを持っています、

 var req = WebRequest.CreateRequest("http://someUrl");
 ((HttpWebRequest)req).Contentlenght = ...;
于 2010-08-26T19:24:50.713 に答える