【问题标题】:How to created builder for nested objects如何为嵌套对象创建构建器
【发布时间】:2021-10-07 10:40:31
【问题描述】:

我正在寻找一种方法来创建可以创建嵌套对象的构建器。

我已经读过这个话题:Builder pattern with nested objects 但这种方法是修改模型/实体,我想避免。

我有几个模型: 广告有一个OwnerOwner有一个AddressAddress有一个联系实体。

public class Advertisment
{
    public string AdvertismentId { get; set; }
    public Owner Owner { get; set; }
}
public class Owner
{
    public string Name { get; set; }
    public Address Address { get; set; }
}
public class Address
{
    public string Street { get; set; }
    public Contact Contact { get; set; }
}
public class Contact
{
    public string PhoneNo { get; set; }
}

我想链接这样的方法(或类似的方法):

        var ad = new AdBuilder()
                    .WithId("123")
                    .WithOwner(x =>
                        x.WithName("NameOfOwner")
                        x.WithAddress(x => x.WithStreet("SomeStreet"))
                        .WithContact(y => y.WithPhoneNo("123-345-789"))));

我在这里准备了代码: https://rextester.com/TRN19779

我不想修改模型,因为它们是从 JSON Schema 生成的,我将它们保存在单独的类库中。我将使用上面的构建器将一个对象映射到另一个对象。 我正在准备一个对象发送给第 3 方消费者。

会是这么好的人并提供一点帮助吗?

【问题讨论】:

  • 在我看来,有 7 个 WithXYZ 方法来设置 4 个属性并将对象粘合在一起确实有点矫枉过正。拥有一个可以构建整个层次结构并设置所有值的具有 4 个参数的方法还不够吗?
  • @Peter 我也不相信,但是使用 new 运算符构建一个有点复杂的对象也不能说服我
  • 这只是那个对象的一部分。我正在建造的整个物体要大得多

标签: c# .net design-patterns builder builder-pattern


【解决方案1】:

这可以使用接口来完成。每个子 object 都有一个 interface 用于构造它。例如,Owner 类具有相应的IOwner 接口,允许用户填充Owner class 的属性。

public interface IBuild
{
    Advertisement Build( );
}

public interface IAdvertisement : IBuild
{
    IAdvertisement WithID( string id );
    IAdvertisement WithOwner( Action<IOwner> func );
}

public interface IOwner
{
    IOwner WithName( string name );
    IOwner WithAddress( Action<IAddress> func );
}

public interface IAddress
{
    IAddress WithStreet( string street );
    IAddress WithContact( Action<IContact> func );
}

public interface IContact
{
    IContact WithPhoneNumber( string phoneNumber );
}

public class AdvertisementBuilder : IAdvertisement, IOwner, IAddress, IContact
{
    private readonly Advertisement _advert = new( )
    { Owner = new( ) { Address = new( ) { Contact = new( ) } } };

    private AdvertisementBuilder( )
    { }

    public static IAdvertisement Create( ) => 
        new AdvertisementBuilder( );        

    public IOwner WithAddress( Action<IAddress> func )
    {
        func( this );
        return this;
    }

    public IAddress WithContact( Action<IContact> func )
    {
        func( this );
        return this;
    }

    public IAdvertisement WithID( string id )
    {
        _advert.AdvertisementId = id;
        return this;
    }

    public IOwner WithName( string name )
    {
        _advert.Owner.Name = name;
        return this;
    }

    public IAdvertisement WithOwner( Action<IOwner> func )
    {
        func( this );
        return this;
    }

    public IContact WithPhoneNumber( string phoneNumber )
    {
        _advert.Owner.Address.Contact.PhoneNo = phoneNumber;
        return this;
    }

    public IAddress WithStreet( string street )
    {
        _advert.Owner.Address.Street = street;
        return this;
    }

    // Here you can do some validation and make sure 
    // any required information has been filled in.
    public Advertisement Build( ) => _advert;
}


static void Main( string[ ] _ )
{
    var advertisement = AdvertisementBuilder.Create( )
        .WithID( "Some id" )
        .WithOwner( o => o.WithName( "Bill" )
            .WithAddress( a => a.WithStreet( "Some street" )
            .WithContact( c => c.WithPhoneNumber( "905-334-5839" ) ) ) )
        .Build( );
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-17
    • 2021-06-09
    • 2015-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-15
    相关资源
    最近更新 更多