Building Don Draper

I have seen and written a few horrible constructors in my life. Some that had more nulls than actual params because there were more optional params than mandatory. In other cases, the constructor did nothing to restrict or sanitize the order of incoming arguments in any way.

When I read Joshua Bloch's insightful Effective Java I learned about the Builder pattern as an effective design pattern.

Let's say we were attempting to build Don Draper with this class

/**
 * There can only be one Don Draper, but let's try to build another one!
 *
 * @author Chaitra Suresh
 */
public class DonDraper {

    private final String cigarette;
    private final String hair;
    private final String smile;
    private final String drink;
    private final String ideas;
    private final Boolean alternateIdentities;
    private final Boolean hasKids;
    private final int numberOfWives;
    private final String clothes;

    public DonDraper(final String cigarette, final String hair, final String smile, final String drink, final String ideas, final Boolean alternateIdentities, final Boolean hasKids, final int numberOfWives, final String clothes) {
        this.cigarette = cigarette;
        this.hair = hair;
        this.smile = smile;
        this.drink = drink;
        this.ideas = ideas;
        this.alternateIdentities = alternateIdentities;
        this.hasKids = hasKids;
        this.numberOfWives = numberOfWives;
        this.clothes = clothes;
    }

    public void speakUp() {
        System.out.println("People were buying cigarettes before Freud was born.");
    }

    public static void main(String[] args) {
        DonDraper don = new DonDraper("luckyStrike", "gelledAndAwesome", "enigmatic", "oldFashioned", "brilliant", true, true, 2, "suitUporGoHome");
        don.speakUp();
    }
}

Couple of problems with this approach

Order of arguments

Don Draper constructor does not check the order the constructor args are passed in. The check is only for the type of the args and not the order

So the client can do something like this

 DonDraper don = new DonDraper("gelledAndAwesome", "luckyStrike", "enigmatic", "oldFashioned", "brilliant", true, true, 2, "suitUporGoHome");

or

  DonDraper don = new DonDraper("luckyStrike", "gelledAndAwesome", "enigmatic", "oldFashioned", "suitUpOrGoHome", true, true, 2, "brilliant");

Optional parameters

If we have some optional parameters we want to include, we will have to include that in the constructor and pass null if we don't have that parameter.

One way to do it is to use telescoping constructors - providing many flavors of constructors and supplying default values or null values for optional parameters. However, this does not scale well when there are different permutations for the arguments and/or the number of arguments is large.

Builder to the rescue

Builders are good way to force the order of arguments as well as provide a clean way to construct an object that takes numerous arguments.

  /**
 * There can only be one Don Draper, but let's try to build another one!
 *
 * @author Chaitra Suresh
 */
public class DonDraper {

    private final String cigarette;
    private final String hair;
    private final String smile;
    private final String drink;
    private final String ideas;
    private final Boolean alternateIdentities;
    private final Boolean hasKids;
    private final int numberOfWives;
    private final String clothes;

    private DonDraper(final Builder builder) {
        cigarette = builder.cigarette;
        hair = builder.hair;
        smile = builder.smile;
        drink = builder.drink;
        ideas = builder.ideas;
        alternateIdentities = builder.alternateIdentities;
        hasKids = builder.hasKids;
        numberOfWives = builder.numberOfWives;
        clothes = builder.clothes;
    }

    public void speakUp() {
        System.out.println("People were buying cigarettes before Freud was born.");
    }

    public static void main(String[] args) {

        DonDraper don = new Builder()
                            .withCigarette("luckyStrike")
                            .withHair("gelledAndAwesome")
                            .withSmile("enigmatic")
                            .withDrink("oldFashioned")
                            .withIdeas("brilliant")
                            .withAlternateIdentities(true)
                            .withNumberOfWives(2)
                            .withHasKids(true)
                            .withClothes("suitUpOrGoHome").build();

        don.speakUp();
    }

    public static final class Builder {
        private String cigarette;
        private String hair;
        private String smile;
        private String drink;
        private String ideas;
        private Boolean alternateIdentities;
        private Boolean hasKids;
        private int numberOfWives;
        private String clothes;

        public Builder() {
        }

        public Builder withCigarette(final String val) {
            cigarette = val;
            return this;
        }

        public Builder withHair(final String val) {
            hair = val;
            return this;
        }

        public Builder withSmile(final String val) {
            smile = val;
            return this;
        }

        public Builder withDrink(final String val) {
            drink = val;
            return this;
        }

        public Builder withIdeas(final String val) {
            ideas = val;
            return this;
        }

        public Builder withAlternateIdentities(final Boolean val) {
            alternateIdentities = val;
            return this;
        }

        public Builder withHasKids(final Boolean val) {
            hasKids = val;
            return this;
        }

        public Builder withNumberOfWives(final int val) {
            numberOfWives = val;
            return this;
        }

        public Builder withClothes(final String val) {
            clothes = val;
            return this;
        }

        public DonDraper build() {
            return new DonDraper(this);
        }
    }
}

Having a builder lets us do the following -

  • It does not matter what order we pass in the arguments, so it is less error prone.
  • You can put in other checks that sanitize the input before you build Don Draper. For example, you could say that if the client did not pass gelledAndAwesome for hair, you could throw an IllegalArgumentException and indicate which arg was incorrect.
  • You can have optional parameters with default values if no args were passed
/**
 * There can only be one Don Draper, but let's try to build another one!
 *
 * @author Chaitra Suresh
 */
public class DonDraper {

    private final String cigarette;
    private final String hair;
    private final String smile;
    private final String drink;
    private final String ideas;
    private final Boolean alternateIdentities;
    private final Boolean hasKids;
    private final int numberOfWives;
    private final String clothes;

    //Optional param
    private String lighterForCigarette = "borrowFromSomeone";

    private DonDraper(final Builder builder) {
        cigarette = builder.cigarette;
        hair = builder.hair;
        smile = builder.smile;
        drink = builder.drink;
        ideas = builder.ideas;
        alternateIdentities = builder.alternateIdentities;
        hasKids = builder.hasKids;
        numberOfWives = builder.numberOfWives;
        clothes = builder.clothes;

        if (null != builder.lighterForCigarette) {
            lighterForCigarette = builder.lighterForCigarette;
        }
        if (!hair.equals("gelledAndAwesome")) {
            throw new IllegalArgumentException("bad hair for Don Draper!");
        }

    }

    public void speakUp() {
        System.out.println("People were buying cigarettes before Freud was born.");
    }

    public static void main(String[] args) {

        try {
            DonDraper don = new Builder()
                    .withCigarette("luckyStrike")
                    .withHair("gelledAndAwesome")
                    .withSmile("enigmatic")
                    .withDrink("oldFashioned")
                    .withIdeas("brilliant")
                    .withAlternateIdentities(true)
                    .withNumberOfWives(2)
                    .withHasKids(true)
                    .withClothes("suitUpOrGoHome").build();

            don.speakUp();
        } catch (Exception ex) {
            System.out.println("Uhoh! No Don Draper for you!");
        }
    }

    public static final class Builder {
        private String cigarette;
        private String hair;
        private String smile;
        private String drink;
        private String ideas;
        private Boolean alternateIdentities;
        private Boolean hasKids;
        private int numberOfWives;
        private String clothes;
        private String lighterForCigarette;

        public Builder() {
        }

        public Builder withCigarette(final String val) {
            cigarette = val;
            return this;
        }

        public Builder withHair(final String val) {
            hair = val;
            return this;
        }

        public Builder withSmile(final String val) {
            smile = val;
            return this;
        }

        public Builder withDrink(final String val) {
            drink = val;
            return this;
        }

        public Builder withIdeas(final String val) {
            ideas = val;
            return this;
        }

        public Builder withAlternateIdentities(final Boolean val) {
            alternateIdentities = val;
            return this;
        }

        public Builder withHasKids(final Boolean val) {
            hasKids = val;
            return this;
        }

        public Builder withNumberOfWives(final int val) {
            numberOfWives = val;
            return this;
        }

        public Builder withClothes(final String val) {
            clothes = val;
            return this;
        }

        public Builder withLighterForCigarette(final String val) {
            lighterForCigarette = val;
            return this;
        }

        public DonDraper build() throws Exception{
            return new DonDraper(this);
        }
    }
}

If you did not pass lighterForCigarette as illustrated above, we will simply use the default value which is borrowFromSomeone.

Note that, when we invoke the builder, we don't even call .withLighterForCigarette.

Imagine if you have a ton of such optional parameters. This approach would let you omit all that you don't want to pass and nicely sanitize the args you decide to pass. Additionally, if you're using an IDE like IntelliJ you can autogenerate this with the refactor > replace constructor with builder menu.

Chaitra Suresh

Chaitra Suresh

Engineer, Mom, Cook, Musician
California