1

以下のサンプルコンソールアプリケーションを考えます。

質問#1:.Name()がtypeof OranizationBuilderを返すのに、.Write()がCorporationBuilderを呼び出すのはなぜですか?

質問#2:.Name()を取得してtypeof CorporationBuilderを返す方法は?

namespace MyCompany
{
    using System;

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Environment.NewLine);

            Factory.Organization()
                    .ID(33)
                    .Name("Oranization A")
                    .Write();

            Console.WriteLine("\n----------------------------\n");

            Factory.Corporation()
                    .Date(DateTime.Today)     // Pass
                    .ID(44)
                    .Name("Company B")
                    // .Date(DateTime.Today)  // Fail
                    .Write();

            // QUESTION #1: Why does .Name() return typeof OranizationBuilder, 
            //              but .Write() calls CorporationBuilder?

            // QUESTION #2: How to get .Name() to return typeof CorporationBuilder?


            Console.ReadLine();
        }
    }

    /* Business Classes */

    public abstract class Contact
    {
        public int ID { get; set; }
    }

    public class Organization : Contact
    {
        public string Name { get; set; }
    }

    public class Corporation : Organization
    {
        public DateTime Date { get; set; }
    }


    /* Builder */

    public abstract class ContactBuilder<TContact, TBuilder>
        where TContact : Contact
        where TBuilder : ContactBuilder<TContact, TBuilder>
    {
        public ContactBuilder(TContact contact)
        {
            this.contact = contact;
        }

        private TContact contact;

        public TContact Contact
        {
            get
            {
                return this.contact;
            }
        }

        public virtual TBuilder ID(int id)
        {
            this.Contact.ID = id;
            return this as TBuilder;
        }

        public virtual void Write()
        {
            Console.WriteLine("ID   : {0}", this.Contact.ID);
        }
    }

    public class OrganizationBuilder : ContactBuilder<Organization, OrganizationBuilder>
    {
        public OrganizationBuilder(Organization contact) : base(contact) { }

        public virtual OrganizationBuilder Name(string name)
        {
            (this.Contact as Organization).Name = name;
            return this;
        }

        public override void Write()
        {
            base.Write();
            Console.WriteLine("Name : {0}", this.Contact.Name);
        }
    }

    public class CorporationBuilder : OrganizationBuilder
    {
        public CorporationBuilder(Corporation contact) : base(contact) { }

        public virtual CorporationBuilder Date(DateTime date)
        {
            // Cast is required, but need this.Contact to be typeof 'C'
            (this.Contact as Corporation).Date = date;
            return this;
        }

        public override void Write()
        {
            base.Write();
            Console.WriteLine("Date : {0}", (this.Contact as Corporation).Date.ToShortDateString());
        }
    }

    /* Factory */

    public class Factory
    {
        public static OrganizationBuilder Organization()
        {
            return new OrganizationBuilder(new Organization());
        }

        public static CorporationBuilder Corporation()
        {
            return new CorporationBuilder(new Corporation());
        }
    }
}

編集/更新

これが解決策の最初の試みです(以下を参照)。ただし、私はFactory内で立ち往生しており、.Organization()および.Corporation()メソッドタイプを構成する方法がわかりません。

namespace MyCompany
{
    using System;

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Environment.NewLine);

            Factory.Organization()
                    .ID(33)
                    .Name("Oranization A")
                    .Write();

            Console.WriteLine("\n----------------------------\n");

            Factory.Corporation()
                    .ID(44)
                    .Name("Company B")
                    .Date(DateTime.Today)
                    .Write();

            Console.ReadLine();
        }
    }


    /* Business Classes */

    public abstract class Contact
    {
        public int ID { get; set; }
    }

    public class Organization : Contact
    {
        public string Name { get; set; }
    }

    public class Corporation : Organization
    {
        public DateTime Date { get; set; }
    }


    /* Builder */

    public abstract class ContactBuilder<TContact, TBuilder>
        where TContact : Contact
        where TBuilder : ContactBuilder<TContact, TBuilder>
    {
        public ContactBuilder(TContact contact)
        {
            this.contact = contact;
        }

        private TContact contact;

        public TContact Contact
        {
            get
            {
                return this.contact;
            }
        }

        public virtual TBuilder ID(int id)
        {
            this.Contact.ID = id;
            return this as TBuilder;
        }

        public virtual void Write()
        {
            Console.WriteLine("ID   : {0}", this.Contact.ID);
        }
    }

    public class OrganizationBuilder<TOrganization, TBuilder> : ContactBuilder<TOrganization, TBuilder> where TOrganization : Organization where TBuilder : OrganizationBuilder<TOrganization, TBuilder>
    {
        public OrganizationBuilder(TOrganization contact) : base(contact) { }

        public virtual TBuilder Name(string name)
        {
            this.Contact.Name = name;
            return this as TBuilder;
        }

        public override void Write()
        {
            base.Write();
            Console.WriteLine("Name : {0}", this.Contact.Name);
        }
    }

    public class CorporationBuilder<TCorporation, TBuilder> : OrganizationBuilder<TCorporation, TBuilder> where TCorporation : Corporation where TBuilder : CorporationBuilder<TCorporation, TBuilder>
    {
        public CorporationBuilder(TCorporation contact) : base(contact) { }

        public virtual TBuilder Date(DateTime date)
        {
            this.Contact.Date = date;
            return this as TBuilder;
        }

        public override void Write()
        {
            base.Write();
            Console.WriteLine("Date : {0}", this.Contact.Date.ToShortDateString());
        }
    }


    /* Factory */

    public class Factory
    {
        public static OrganizationBuilder<Organization, OrganizationBuilder> Organization()
        {
            return new OrganizationBuilder<Organization, OrganizationBuilder>(new Organization());
        }

        public static CorporationBuilder<Corporation, CorporationBuilder> Corporation()
        {
            return new CorporationBuilder<Corporation, CorporationBuilder>(new Corporation());
        }
    }
}

具体的な問題領域は次のとおりです。

/* Factory */

public class Factory
{
    public static OrganizationBuilder<Organization, OrganizationBuilder> Organization()
    {
        return new OrganizationBuilder<Organization, OrganizationBuilder>(new Organization());
    }

    public static CorporationBuilder<Corporation, CorporationBuilder> Corporation()
    {
        return new CorporationBuilder<Corporation, CorporationBuilder>(new Corporation());
    }
}

OrganizationBuilderとCorportationBuilderを構成するにはどうすればよいですか?

4

2 に答える 2

4

Name参照を返すときは戻りますthis-したがって、インスタンスが実際にのインスタンスである場合CorporationBuilder、その参照は通常どおりに返されます。メソッドが戻り値を返すように宣言されているからといって、それが参照のみを返すOrganizationBuilderことを意味するわけではありません。インスタンスまたは派生クラス(またはもちろん)のインスタンスへの参照を返します。OrganizationBuilderOrganizationBuildernull

次にWriteメソッドが呼び出されると、それは仮想メソッドであるため、オブジェクトの実行時タイプがチェックされ、使用する実装が検索されます。実行時の型はまだCorporationBuilderなので、その型で指定されたオーバーライドが使用されます。

Name()適切な型を返す方法については、基本的に、より多くのジェネリックが必要になります。それは可能ですが、それは苦痛です-私はProtocol Buffersで同様のことをしましたが、それは快適ではありません。OrganizationBuilderでジェネリックをTContact作成し、からへのキャストを介してリターンをTBuilder作成します。次に、一般的なものにするか、から継承します。NameTBuilderthisTBuilderCorporationBuilderOrganizationBuilder<Corporation, CorporationBuilder>

編集:はい、問題があります(以前は忘れていました)。CorporationBuilder再帰的なジェネリックを回避するために、具体的な非ジェネリッククラスも呼び出すことができます。

public class OrganizationBuilder :
    OrganizationBuilder<Organization, OrganizationBuilder>

混乱を避けるために、名前OrganizationBuilderをに変更することもできます:)OrganizationBuilderBase

CorporationBuilder(階層の最下位にある場合は、それ自体がジェネリックである必要はありません。)

ただし、これは非常に複雑になっています。ここでは、少なくとも継承を回避することを検討することをお勧めします。ジェネリックをスクラップし、それから派生する代わりにOrganizationBuilder 持ってください。CorporationBuilder

基本的に、このパターンはしばらくすると常に複雑になります。リーフノードを除いて、すべてのレベルをジェネリックにする必要があります。リーフノードは、すでに見た再帰の問題を回避するために、常に非ジェネリックである必要があります。それは苦痛です。

于 2009-08-29T21:20:17.473 に答える
0

OrganizationBuilderの.Name()関数には、OrganizationBuilderタイプを返すシグネチャがあります。これは、どの派生オブジェクトから呼び出されたかに関係なく行われます。そのため、OrganizationBuilderが返されます。コントラクトビルダーでName()関数をオーバーライドし、名前を別の名前に設定すると、Name()関数がランタイムオブジェクトに作用していることがわかります。

ここで、Name()に目的のビルダーを返す方法を知りたい場合は、ID()メソッドに使用したのと同じ手法に従う必要があります。

編集/更新: まあ、今私はあなたが直面している実際のエラーを理解していません-新しい更新で。あなたが直面している正確なエラーを共有できますか?

ちなみに、このデザインは完全に複雑だと思います。適切なビルダーオブジェクトを返すビルダーメソッドの素晴らしいパターンをサポートするためだけに、これをコンシューマーに提供することはしません。私はもっ​​と単純なアプローチに固執したいと思います。

于 2009-08-29T21:17:29.227 に答える