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());
        }
    }
}

编辑/更新

这是我第一次尝试解决方案(见下文),尽管我被困在工厂内部并且不确定如何配置 .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并不意味着它只返回一个OrganizationBuilder引用。它返回对实例的OrganizationBuilder实例或派生类(或者null,当然)的引用。

然后调用该Write方法时,这是一个虚拟方法,因此检查对象的执行时类型以找到要使用的实现。执行时类型仍然是CorporationBuilder,因此使用该类型中指定的覆盖。

至于如何Name()返回适当的类型——基本上这需要更多的泛型。可以做到,但很痛苦——我在 Protocol Buffers 中做过类似的事情,但这并不令人愉快。你也可以在and中进行OrganizationBuilder泛型,并通过从 to 的转换来返回。然后要么是通用的,要么只是继承自.TContactTBuilderNameTBuilderthisTBuilderCorporationBuilderOrganizationBuilder<Corporation, CorporationBuilder>

编辑:是的,我看到了问题(我之前忘记了)。您可能还希望调用一个具体的非泛型类CorporationBuilder,以避免递归泛型:

public class OrganizationBuilder :
    OrganizationBuilder<Organization, OrganizationBuilder>

您可能还想重命名OrganizationBuilderOrganizationBuilderBase避免混淆:)

CorporationBuilder(如果它位于层次结构的底部,则您不需要是通用的。)

然而,这变得非常复杂。您可能至少要考虑在这里避免继承。废弃泛型,并让OrganizationBuilder 一个CorporationBuilder而不是从它派生。

基本上这种模式总是在一段时间后变得复杂 - 除了叶节点之外,您最终需要每个级别都是通用的,叶节点总是需要是非通用的,以避免您已经看到的递归问题。这是一种痛苦。

于 2009-08-29T21:20:17.473 回答
0

OrganizationBuilder 中的 .Name() 函数具有返回 OrganizationBuilder 类型的签名——无论它是从哪个派生对象调用的。这就是您看到它返回 OrganizationBuilder 的原因。如果您在合约构建器中覆盖 Name() 函数并将名称设置为其他名称,您会注意到 Name() 函数正在作用于您的运行时对象。

现在,如果您想知道如何让 Name() 返回您想要的构建器,您应该遵循与用于 ID() 方法相同的技术。

编辑/更新: 嗯,现在我不明白你所面临的实际错误 - 新的更新。你能分享你面临的确切错误吗?

附带说明:我觉得这个设计非常复杂。我不会为了支持返回适当构建器对象的构建器方法的良好模式而将其提供给我的消费者。我会坚持更简单的方法。

于 2009-08-29T21:17:29.227 回答