Greg Young [MVP]

Sponsors

The Lounge

News

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
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).


Posted Wed, Apr 16 2008 6:22 PM by Greg
Filed under: , ,

[Advertisement]

Comments

Jason Haley wrote Interesting Finds: April 17, 2008
on Thu, Apr 17 2008 10:28 AM
gateway production wrote gateway production
on Sun, Apr 20 2008 2:44 PM

Pingback from  gateway production

main object line wrote main object line
on Sun, Apr 20 2008 8:12 PM

Pingback from  main object line

alt.net Seattle Conference Thoughts (April 18th - April 20th) | Adam Tybor's Blog wrote alt.net Seattle Conference Thoughts (April 18th - April 20th) | Adam Tybor's Blog
on Fri, Apr 25 2008 2:08 AM

Pingback from  alt.net Seattle Conference Thoughts (April 18th - April 20th) | Adam Tybor's Blog

Christian Crowhurst wrote re: DDDD 6 [Fluent Builders, Alternate Ending]
on Fri, Apr 25 2008 3:19 PM

Hi Greg,

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

nat.truemesh.com/.../000724.html

Christian

Greg wrote re: DDDD 6 [Fluent Builders, Alternate Ending]
on Fri, Apr 25 2008 5:32 PM

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

Christian Crowhurst wrote re: DDDD 6 [Fluent Builders, Alternate Ending]
on Sat, Apr 26 2008 4:33 AM
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; }
Greg wrote re: DDDD 6 [Fluent Builders, Alternate Ending]
on Sat, Apr 26 2008 3:16 PM

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

Elegant Code » Test Data Builders Refined wrote Elegant Code » Test Data Builders Refined
on Sat, Apr 26 2008 7:08 PM

Pingback from  Elegant Code » Test Data Builders Refined

Christian Crowhurst wrote re: DDDD 6 [Fluent Builders, Alternate Ending]
on Mon, Apr 28 2008 3:52 PM
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
John Teague's Blog wrote Encapsulating Test Data and Expectations
on Tue, Aug 12 2008 11:13 PM

I love it when I find new ways to improve my testing ability. In this case, it's not really new,

Add a Comment

(required)  
(optional)
(required)  
Remember Me?