Click here to Skip to main content
13,004,188 members (65,306 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as


4 bookmarked
Posted 16 Apr 2010

Writing Readable Code - Complex Object Construction

, 16 Apr 2010
Rate this:
Please Sign up or sign in to vote.
(Cross post from IRefactor)Once upon a time, there was a class called Invoice.

(Cross post from IRefactor)

Once upon a time, there was a class called Invoice. Its responsibility was to calculate a final price being presented to the customer.

Time went on; The autumn passed, the winter fade out and the spring was already at the door and our class started to rust. Each time a developer found a new set of relevant parameters (that should have been passed to the Invoice class) he added a new constructor, to support them.

And so it happened, that after a while, two fellows stumbled on the class:

"What's that rusty thing, my dear?" said G.Ollum "I don't understand; What constructor should I invoke on the Invoice class? Does it matter how should I create the Invoice object? The class has more than 20 constructors; How in the world, somebody would understand what to do?"

"Let me see, what's the problem" said D.Eagol

public class Invoice
    public Customer Customer { get; private set; }
    public DateTime ContractDate { get; private set; }
    public TimeSpan ServiceContinuum { get; private set; }
    public double BaseFee { get; private set; }
    public double ServiceFee { get; private set; }
     //... a lot of other members ...
     public Invoice(Customer customer)
       Customer = customer;
     public Invoice(Customer customer, DateTime contractDate)
       : this(customer)
     public Invoice(Customer customer, DateTime contractDate, double baseFee)
       : this(customer, contractDate)
     public Invoice(Customer customer, TimeSpan serviceContinuum, double serviceFee)
       : this(customer)
     //... a lot of other constructors ...

"It seems that you are right G.Olum", said D.Eagol after looking at the code, "You have a lot of different constructors, each instantiates a slightly different type of Invoice object, mainly because there are a lot of optional parameters to the Invoice class. There are also required ones; Look at the Customer parameter being accepted in each and every constructor."

"Moreover", continued D.Eagol, "The world has changed and something that shouldn't have been forgotten, was lost."

"What's that, my dear?", inquired G.Olum

"Well, you cannot name your constructors. That's why it is so difficult for your to find how to instantiate the class. If you were supplied methods with meaningful, intention revealing names then you would be able to create a required object", explained D.Eagol

"So, what can I do?", cried G.Olum

"Let's summarise what you really want to do and then see how to accomplish it", said D.Eagol

You want To

  • Create a complex object, with:
    • A few required parameters
    • A few (or many) optional parameters

You Don't Want To

  • Use constructors - they are nameless and we fear of them

So, let's create an internal class whose sole responsibility will be to shadow that complex creation.

Our inner class will provide a meaningful method name for each optional parameter of the Invoice class. The required parameters will be passed to one(and only one) inner's class constructor. Since, that inner class deals with construction, let's call him a Builder.

Here is, how it should look:

public sealed class Invoice
      public Customer Customer { get; private set; }
      public DateTime ContractDate { get; private set; }
      public TimeSpan ServiceContinuum { get; private set; }
      public double BaseFee { get; private set; }
      public double ServiceFee { get; private set; }
       private Invoice()
       public class Builder
         private Customer customer;
         private DateTime contractDate;
         private TimeSpan serviceContinuum;
         private double baseFee;
         private double serviceFee;
           public Builder(Customer customer)
            this.customer = customer;
          public Builder SignedOn(DateTime signDate)
            this.contractDate = signDate;
             return this;
          public Builder ServiceAgreementForPeriodOf(TimeSpan serviceContinuum)
            this.serviceContinuum = serviceContinuum;
             return this;
          public Builder ServiceBaseFee(double baseFee)
            this.baseFee = baseFee;
             return this;
          public Builder ServiceFee(double serviceFee)
            this.serviceFee = serviceFee;
             return this;
          public Invoice Build()
            Invoice invoice = new Invoice 
               Customer = this.customer, 
               ContractDate = this.contractDate, 
               ServiceContinuum = this.serviceContinuum, 
               BaseFee = this.baseFee, 
               ServiceFee = this.serviceFee 
             return invoice;

"You see", continued D.Eagol, "That's what you have accomplished:"

  • The Invoice's constructor is private - only the Builder can return an Invoice, through its Build method
  • The Builder's constructor receives the Invoice's required parameter: Customer
  • The Builder provides meaningful method names for eachof the Invoice's optional parameters and utilizes Fluent interface in order to provide a more readable form of setting those parameters
"And here, how the instantiation looks like:"
Invoice.Builder invoiceBuilder = new Invoice.Builder(Customer.CustomerA);
        Invoice invoice = invoiceBuilder
                          .SignedOn(Date("4/7/2010 08:00:00 PM"))

"Now it really shines!", finished D.Eagol, "What do you say G.Olum?"

"Give us that, D.eagol, my love... It's my birthday...and I wants it!", gurgled G.Olum.


This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)


About the Author

Uri Lavi
Israel Israel
Uri Lavi is a development lead with extensive experience in Data Intensive, Business Compound, Distributed and Scalable Software Systems. Uri specializes in mentoring, coaching and consulting for complex software engineering topics, among which: Software Architecture, Design Patterns & Refactoring.

You may also be interested in...

Comments and Discussions

GeneralMy vote of 3 Pin
taras_b16-Feb-11 13:33
membertaras_b16-Feb-11 13:33 
GeneralI still don't see why we need this. Pin
Member 389508821-Oct-10 20:41
memberMember 389508821-Oct-10 20:41 
GeneralRe: I still don't see why we need this. Pin
taras_b16-Feb-11 13:32
membertaras_b16-Feb-11 13:32 
GeneralVariables of type Invoice.Builder Pin
supercat916-Apr-10 12:09
membersupercat916-Apr-10 12:09 
GeneralRe: Variables of type Invoice.Builder Pin
Uri Lavi16-Apr-10 18:39
memberUri Lavi16-Apr-10 18:39 
GeneralRe: Variables of type Invoice.Builder Pin
supercat919-Apr-10 5:22
membersupercat919-Apr-10 5:22 
Uri Lavi wrote:
Hence it's impossible to use the same Builder to continue to modify the Invoice class and it's impossible to have the fields set from the first object.

Agreed. The idea with my example was that the static CreateNew method of Invoice would return an object of type Invoice.Builder, which would in turn support an implicit widening conversion back to Invoice (the "conversion" creating a new immutable invoice type). Thus, saying anInvoice = Invoice.CreateNew.withPurchases(somePurchases).withCoupon(someCoupon) would create an Invoice.Builder object, perform withPurchases(somePurchases) on it, then perform withCoupon(someCoupon) on it, and then build an (immutable) invoice from the Invoice.Builder object.

While one might favor a slightly more verbose syntax anInvoice = Invoice.Builder.CreateNew.withPurchases(somePurchases).withCoupon(someCoupon).Build I'm not sure the extra verbosity is helpful. Yes, it shows more accurately what's really going on, but I would think the idea of the Fluent interface is to make things look like what one is seeking to accomplish, rather than like the sequence of steps that will be executed to get there.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170627.1 | Last Updated 16 Apr 2010
Article Copyright 2010 by Uri Lavi
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid