Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

DDDD 6 [Fluent Builders, Alternate Ending]

Sitting with an awesome view of Seattle … Last night I spent some time over at Eleutian with Aaron and during our discussions we talked a bit about Fluent Builders. He brought up an interesting idea to me of whether or not builders should be immutable and I think I agree with his mutable version so I am going to provide another way of implementing the Fluent Builders from DDDD 5 [Messages have Fluent Builders].

 

The basics of the discussion revolve around the methods used for setting fields in the builder. It is best seen in code comparing the builder in the last post of …

 

	public class EquityQuoteMessageBuilder {
internal readonly ExchangeGateways m_Gateway;
internal readonly string m_Security;
internal readonly DateTime m_Time;
internal readonly decimal m_BidPrice;
internal readonly decimal m_AskPrice;


[DebuggerStepThroughAttribute]
public EquityQuoteMessageBuilder OnGateway(ExchangeGateways _Gateway) {
return new InsertEquityQuoteCommandBuilder(_Gateway,m_Security,m_Time,m_BidPrice,m_AskPrice);
}

[DebuggerStepThroughAttribute]
public EquityQuoteMessageBuilder ForSecurity(string _Symbol) {
return new InsertEquityQuoteCommandBuilder(m_Gateway,_Symbol,m_Time,m_BidPrice,m_AskPrice);
}

[DebuggerStepThroughAttribute]
public EquityQuoteMessageBuilder At(DateTime _Time) {
return new InsertEquityQuoteCommandBuilder(m_Gateway,m_Security,_Time,m_BidPrice,m_AskPrice);
}

[DebuggerStepThroughAttribute]
public EquityQuoteMessageBuilder WithSpreadOf(decimal _BidPrice, decimal _AskPrice) {
return new InsertEquityQuoteCommandBuilder(m_Gateway,m_Security,m_Time,_BidPrice,_AskPrice);
}

private EquityQuoteMessageBuilder(ExchangeGateways _Gateway,string _Symbol,DateTime _Time,decimal _BidPrice,decimal _AskPrice) {
m_Gateway = _Gateway;
m_Security = _Symbol;
m_Time = _Time;
m_BidPrice = _BidPrice;
m_AskPrice = _AskPrice;
}

public EquityQuoteMessageBuilder {}

public static implicit operator EquityQuoteMessage(EquityQuoteMessageBuilder _Builder) {
return new InsertEquityQuoteCommand(_Builder.m_Gateway, _Builder.m_Security, _Builder.m_Time, _Builder.m_BidPrice, _Builder.m_AskPrice);
}
}

With the modified version being ….

	public class EquityQuoteMessageBuilder {
internal ExchangeGateways m_Gateway;
internal string m_Security;
internal DateTime m_Time;
internal decimal m_BidPrice;
internal decimal m_AskPrice;


[DebuggerStepThroughAttribute]
public EquityQuoteMessageBuilder OnGateway(ExchangeGateways _Gateway) {
m_Gateway = _Gateway;
return this;
}

[DebuggerStepThroughAttribute]
public EquityQuoteMessageBuilder ForSecurity(string _Symbol) {
m_Symbol = _Symbol;
return this;
}

[DebuggerStepThroughAttribute]
public EquityQuoteMessageBuilder At(DateTime _Time) {
m_Time = _Time;
return this;
}

[DebuggerStepThroughAttribute]
public EquityQuoteMessageBuilder WithSpreadOf(decimal _BidPrice, decimal _AskPrice) {
m_BidPrice = _BidPrice;
m_AskPrice = _AskPrice;
return this;
}

public EquityQuoteMessageBuilder {}

public static implicit operator EquityQuoteMessage(EquityQuoteMessageBuilder _Builder) {
return new InsertEquityQuoteCommand(_Builder.m_Gateway, _Builder.m_Security, _Builder.m_Time, _Builder.m_BidPrice, _Builder.m_AskPrice);
}
}

 

The main difference between the two implementations of the Fluent Builder being whether or not the builder itself is mutable. This version is much more terse but the question remains for me of … Do I care? I am generating them … The one place there is quite a bit of benefit is in terms of performance if this is being called often in production code as it will create many less objects and the JIT could be more intelligent about inlining.

 

My original reason for making the builders immutable was for some odd edge conditions like

 

InsertOrderMessageBuilder AlreadyHasSecurityAndBroker = New.InsertOrder.ForSecurity(“RIM”).FromBroker(0);

InsertOrderMessage FirstMessage = AlreadyHasSecurityAndBroker.ToBuy(1000).AtPrice(15.00).AcceptingOnlyCash;

InsertOrderMessage SecondMessage = AlreadyHasSecurityAndBroker.ToSell(1000).AtPrice(15.50);

 

But after thinking about this a bit I could do something similar using a mutable builder … I could extract a method that returned me the AlreadyHasSecurityAndBroker version of the builder to something similar to an object mother. I will have to think a bit more about this but as of now I am considering moving my immutable builders to their mutable brethren.

 

Another difference that we found in implementation was my use of the DebuggerStepThrough attribute. For those who are not familiar with it; it tells the debugger to not step into this method … so if I have a long line like ..

 

InsertOrderMessage message = New.InsertOrder.ForSecurity(“RIM”).FromBroker(12).ToBuy(2000).AtPrice(15.00);

 

and I set a break point on it … then hit F11 to step into the code, it will bypass all of the short “setter” calls and take me directly to the implicit cast which is generally where my problem would be happening as the builder doesn’t really care about its state. I really like the way this works! It is a good tool to remember for scenarios like this 

 

 

A side discussion came up with someone else in regard to why I define the internal state of the builder as internal and not private. The reason why I do this is because I write (read: generate, the reason why I generate tests is that these classes are generated then manually edited from that point forward) tests for the builders and want to check the fields one by one when issuing a set … As an example of this kind of test consider:

 

		[Test]
public void Should_Set_Gateway() {
EquityQuoteMessageBuilder builder = new EquityQuoteMessageBuilder().OnGateway(ExchangeGateways.CNQToronto);
Assert.AreEqual(ExchangeGateways.CNQToronto,builder.m_Gateway);
}

 

I could just as easily put properties on the builder to expose its internal state or could make the fields public but when a builder is being used outside of these tests I don’t want code to be able to access the internal state of the builder (I like to consider them write only). The need to have these things working properly kind of fits in with the want to have the debugger step through the code (since the code is simple and I have tests to make sure the simple code is working, I can safely guess that I wouldn’t need to be debugging the code).

This entry was posted in DDD, DDDD, Featured. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

7 Responses to DDDD 6 [Fluent Builders, Alternate Ending]

  1. thenightstoker says:

    on the inside of you creation i have him in front of me at lest for the next month and a half will keep you updated

    happy chest playing

  2. Christian Crowhurst says:

    Greg, I’m still interested in seeing the code gen solution for those builders :-)

    I’m hopeful that I’m going to be doing something similar real soon so your solution could really give me a head start.

    C

  3. Greg says:

    I like the clone method … I will have to give that a try.

  4. Christian Crowhurst says:

    Yeah the build method should be replaced by the implicit conversion operator.

    >from looking It’s pretty much the same thing suggested at the end (using a factory method)?

    Actually I hadn’t noticed the products.But().WithX factory method! I kinda like that when reusing builders to define common state.

    The way I applied the advise was to use a copy constructor to give me my builder methods that look like:

    public SchemeBuilder WithMaxPayableDays(int days)
    {
    SchemeBuilder result = new SchemeBuilder(this);
    result._maxPayableDays = days;
    return result;
    }

    To make the code a little more obvious I have started writing a Clone method instead of using the copy constructor directly. This gives:

    public SchemeBuilder WithMaxPayableDays(int days)
    {
    SchemeBuilder result = Clone();
    result._maxPayableDays = days;
    return result;
    }

  5. Greg says:

    Christian … thanks for link … from looking It’s pretty much the same thing suggested at the end (using a factory method)?

    The copy constructor isn’t a bad option either though.

     

    I have to say the one thing I really hate about that example is the .Build()

  6. Christian Crowhurst says:

    Hi Greg,
    Check out the blog post below as it solves the aliasing problem you describe while allowing a more terse builder code:

    http://nat.truemesh.com/archives/000724.html

    Christian

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>