mercredi 13 novembre 2019

Merging Faceted Builder and Recursive Generic Builder patterns

I have a task of creating XML messages from templates. Messages share the same header, but have different content. I used Fluent Recursive Generic Builder pattern (or 2, or SO questions on generic builders) to create/modify these messages.

public abstract class MessageBuilderBase<T> where T : IsoMessageBuilderBase<T>
{
    protected XDocument _Message;
    protected T _this;

    public MessageBuilderBase(XDocument doc)
    {
        _Message = doc;
        _this = (T)this;
    }

    public XDocument Build() => _Message;

    public T SetTo(String toAddress)
    {
        // ... setting header field //
        return _this;
    }
}

public MessageType1Builder<T> : MessageBuilderBase<T> where T : MessageType1Builder<T>
{
    public MessageType1Builder(XDocument message) : base(message) { }

    public T SetMessageType1Property1(string value)
    {
        // set value //
        return _this;
    }
}

public MessageType2Builder<T> : MessageBuilderBase<T> where T : MessageType2Builder<T>
{
    public MessageType2Builder(XDocument message) : base(message) { }

    public T SetMessageType2Property1(string value)
    {
        // set value //
        return _this;
    }
}

I would like to split header and body in two different sections, so that I could write something like:

new MessageType1Builder(templateMessage).
    Header.
        SetTo(toValue).
    Body.
        SetMessageType1Property1(p1Value).
    Build()

For that I tried to use approach described in Faceted Builder pattern, but I got stuck on the Header and Body property declarations with the errors like Cannot convert 'MessageBodyBuilder<T>' to 'T', or 'T' cannot be used as generic parameter 'T'

"Updated" code:

public abstract class MessageBuilderBase<T> where T : IsoMessageBuilderBase<T>
{
    protected XDocument _Message;
    protected T _this;

    public MessageBuilderBase(XDocument doc)
    {
        _Message = doc;
        _this = (T)this;
    }

    public T Header => MessageHeaderBuilder(_Message);
    public MessageBodyBuilder<T> Body => MessageBodyBuilder(_Message)

    public XDocument Build() => _Message;
}

public MessageHeaderBuilder<T> : MessageBuilderBase<T> where T : MessageHeaderBuilder<T>
{
    public T SetTo(String toAddress)
    {
        // ... setting header field
        return _this;
    }
}

public MessageBodyBuilder<T> : MessageBuilderBase<T> where T : MessageHeaderBuilder<T>
{
    // Common Methods
}

public MessageType1Builder<T> : MessageBodyBuilder<T> where T : MessageType1Builder<T>
{
    public MessageType1Builder(XDocument message) : base(message) { }

    public T SetMessageType1Property1(string value)
    {
        // set value
        return _this;
    }
}

public MessageType2Builder<T> : MessageBodyBuilder<T> where T : MessageType2Builder<T>
{
    public MessageType2Builder(XDocument message) : base(message) { }

    public T SetMessageType2Property1(string value)
    {
        // set value
        return _this;
    }
}

Can these two patterns be applied together? Am I missing something critical in my understanding of C# generics, and these two patterns?

Aucun commentaire:

Enregistrer un commentaire