Saturday 28 July 2012

Step Builder pattern

Recently I wanted to implement a builder in my Grep4j API, but, as already happened in the past with the builder or other creational patterns, I was not completely satisfied with the result. A Builder pattern is a design you implement when you want to separate the creation of an object from its representation. For example, let say you have a Java Panino object composed of few ingredients:

package com.stepbuilder.bar;
import java.util.List;
public class Panino {

 private final String name;
 private String breadType;
 private String fish;
 private String cheese;
 private String meat;
 private List vegetables;

 public Panino(String name) {
  this.name = name;
 }

 public String getBreadType() {
  return breadType;
 }

 public void setBreadType(String breadType) {
  this.breadType = breadType;
 }

 public String getFish() {
  return fish;
 }

 public void setFish(String fish) {
  this.fish = fish;
 }

 public String getCheese() {
  return cheese;
 }

 public void setCheese(String cheese) {
  this.cheese = cheese;
 }

 public String getMeat() {
  return meat;
 }

 public void setMeat(String meat) {
  this.meat = meat;
 }

 public List getVegetables() {
  return vegetables;
 }

 public void setVegetables(List vegetables) {
  this.vegetables = vegetables;
 }

 public String getName() {
  return name;
 }

 @Override
 public String toString() {
  return "Panino [name=" + name + ", breadType=" + breadType + ", fish="
    + fish + ", cheese=" + cheese + ", meat=" + meat
    + ", vegetables=" + vegetables + "]";
 }
 
 
}
Now, in order to create a Panino you can write your Builder class that, more or less, will look like the following.

package com.stepbuilder.bar;
import java.util.ArrayList;
import java.util.List;
public class PaninoBuilder {
 
 private String name;
 private String breadType;
 private String fish;
 private String cheese;
 private String meat;
 private List vegetables = new ArrayList();

 public PaninoBuilder paninoCalled(String name){
  this.name = name;
  return this;
 }
 
 public PaninoBuilder breadType(String breadType){
  this.breadType = breadType;
  return this;
 }
 
 public PaninoBuilder withFish(String fish){
  this.fish = fish;
  return this;
 }
 
 public PaninoBuilder withCheese(String cheese){
  this.cheese = cheese;
  return this;
 }
 
 public PaninoBuilder withMeat(String meat){
  this.meat = meat;
  return this;
 }
 
 public PaninoBuilder withVegetable(String vegetable){  
  vegetables.add(vegetable);
  return this;
 }
 
 public Panino build(){  
  Panino panino = new Panino(name);
  panino.setBreadType(breadType);
  panino.setCheese(cheese);
  panino.setFish(fish);
  panino.setMeat(meat);
  panino.setVegetables(vegetables);
  return panino;
 }
 }
A user will be then able to nicely build a Panino using this builder.

package com.stepbuilder.bar.client;
import com.stepbuilder.bar.Panino;
import com.stepbuilder.bar.PaninoBuilder;
public class Bar {

 public static void main(String[] args) {
  Panino marcoPanino = new PaninoBuilder().paninoCalled("marcoPanino")
    .breadType("baguette").withCheese("gorgonzola").withMeat("ham")
    .withVegetable("tomatos").build();

  System.out.println(marcoPanino);
 }
}
So far so good.
But what I don't like of the traditional Builder pattern is the following:


  • It does not really guide the user through the creation.
  • A user can always call the build method in any moment, even without the needed information. 
  • There is no way to guide the user from a creation path instead of another based on conditions. 
  • There is always the risk to leave your object in an inconsistent state.
  • All methods are always available, leaving the responsibility of which to use and when to use to the user who did not write the api.

For instance, in the Panino example, a user could write something like this:

package com.stepbuilder.bar.client;
import com.stepbuilder.bar.Panino;
import com.stepbuilder.bar.PaninoBuilder;
public class Bar {

 public static void main(String[] args) {
  Panino marcoPanino = new PaninoBuilder().paninoCalled("marcoPanino")
    .withCheese("gorgonzola").build();

  // or
  marcoPanino = new PaninoBuilder().withCheese("gorgonzola").build();
  // or
  marcoPanino = new PaninoBuilder().withMeat("ham").build();
  // or
  marcoPanino = new PaninoBuilder().build();
 }
}
All the above panino instances are wrong, and the user will not know until runtime when he will use the Panino object.
You can put a validation in the build method of course, but still a user will be not able to recover from a badly builder usage.
You could also put default values around all the required properties, but then the readability of the code will be lost ( new PaninoBuilder().build(); what are you building here?) and often you really need some input from the user (a user and password for a connection for example).

So here is my solution called Step builder, an extension of the Builder patter that fully guides the user through the creation of the object with no chances of confusion.


package com.stepbuilder.bar;
import java.util.ArrayList;
import java.util.List;
public class PaninoStepBuilder {

        public static FirstNameStep newBuilder() {
                return new Steps();
        }

        private PaninoStepBuilder() {
        }

        /**
         * First Builder Step in charge of the Panino name. 
         * Next Step available : BreadTypeStep
         */
        public static interface FirstNameStep {
                BreadTypeStep paninoCalled(String name);
        }

        /**
         * This step is in charge of the BreadType. 
         * Next Step available : MainFillingStep
         */
        public static interface BreadTypeStep {
                MainFillingStep breadType(String breadType);
        }

        /**
         * This step is in charge of setting the main filling (meat or fish). 
         * Meat choice : Next Step available : CheeseStep 
         * Fish choice : Next Step available : VegetableStep
         */
        public static interface MainFillingStep {
                CheeseStep meat(String meat);

                VegetableStep fish(String fish);
        }

        /**
         * This step is in charge of the cheese. 
         * Next Step available : VegetableStep
         */
        public static interface CheeseStep {
                VegetableStep noCheesePlease();

                VegetableStep withCheese(String cheese);
        }

        /**
         * This step is in charge of vegetables. 
         * Next Step available : BuildStep
         */
        public static interface VegetableStep {
                BuildStep noMoreVegetablesPlease();

                BuildStep noVegetablesPlease();

                VegetableStep addVegetable(String vegetable);
        }

        /**
         * This is the final step in charge of building the Panino Object.
         * Validation should be here.
         */
        public static interface BuildStep {
                Panino build();
        }

        private static class Steps implements FirstNameStep, BreadTypeStep, MainFillingStep, CheeseStep, VegetableStep, BuildStep {

                private String name;
                private String breadType;
                private String meat;
                private String fish;
                private String cheese;
                private final List<String> vegetables = new ArrayList<String>();

                public BreadTypeStep paninoCalled(String name) {
                        this.name = name;
                        return this;
                }

                public MainFillingStep breadType(String breadType) {
                        this.breadType = breadType;
                        return this;
                }

                public CheeseStep meat(String meat) {
                        this.meat = meat;
                        return this;
                }

                public VegetableStep fish(String fish) {
                        this.fish = fish;
                        return this;
                }

                public BuildStep noMoreVegetablesPlease() {
                        return this;
                }

                public BuildStep noVegetablesPlease() {
                        return this;
                }

                public VegetableStep addVegetable(String vegetable) {
                        this.vegetables.add(vegetable);
                        return this;
                }

                public VegetableStep noCheesePlease() {
                        return this;
                }

                public VegetableStep withCheese(String cheese) {
                        this.cheese = cheese;
                        return this;
                }

                public Panino build() {
                        Panino panino = new Panino(name);
                        panino.setBreadType(breadType);
                        if (fish != null) {
                                panino.setFish(fish);
                        } else {
                                panino.setMeat(meat);
                        }
                        if (cheese != null) {
                                panino.setCheese(cheese);
                        }
                        if (!vegetables.isEmpty()) {
                                panino.setVegetables(vegetables);
                        }
                        return panino;
                }

        }
}



The concept is simple:

  1. Write creational steps inner classes or interfaces where each method knows what can be displayed next.
  2. Implement all your steps interfaces in an inner static class.
  3. Last step is the BuildStep, in charge of creating the object you need to build.

The user experience will be much more improved by the fact that he will only see the next step methods available, NO build method until is the right time to build the object.

package com.stepbuilder.bar.client;
import com.stepbuilder.bar.Panino;
import com.stepbuilder.bar.PaninoBuilder;
import com.stepbuilder.bar.PaninoStepBuilder;
public class Bar {

 public static void main(String[] args) {
  Panino solePanino = PaninoStepBuilder.newBuilder()
        .paninoCalled("sole panino")
        .breadType("baguette")
        .fish("sole")
        .addVegetable("tomato")
        .addVegetable("lettece")
        .noMoreVegetablesPlease()
        .build();

  
 }
}
The user will not be able to call the method breadType(String breadType) before calling the paninoCalled(String name) method, and so on.
Plus, using this pattern, if the user choose a fish panino, I will not give him the possibility to add cheese (i'm the chef, I know how to prepare a panino).
In the end, I will have  a consistent Object and user will find extremely easy to use the API because he will have very few and selected choices per time.

We could have different panino traditional builders, FishPaninoBuilder, MeatPaninoBuilder, etc, but still we would face the inconsistent problems and the user will still need to understand exactly what was your idea behind the builder.
With the Step Builder the user will know without any doubt what input is required, making his and your life easier.



19 comments:

  1. Nice article, well done! It offers a way to better communicate to the final programmer "what" is required to build a complex object and not "how", because the "how" is driven by who really has got the knowledge of the object, and that would be the designer of the Step-Builder itself!

    ReplyDelete
    Replies
    1. Indeed its cool pattern and also mentioned by Joshua Bloach in Effective Java. I have also shared one example on my post What is Builder Pattern in Java, you may find useful.

      Delete
  2. It is a cool pattern, i like it.
    I am sure it can become handy in a wide range of scenarios where:

    -The order of execution of the steps must be predefined and preserved at all time(Steps cannot be skipped).

    -A minimum requirements for the object you want to build must be set strictly.(build() cannot be called until the last step is executed).

    Definitely this is a good to have in my pocket swiss knife :)
    Good Work!

    ReplyDelete
  3. This is a great idea. There is nothing worse than working on an API where you may not have access to the source code and you have to go back and forth compiling and running the program until you know the minimum amount of parameters you can get away with.

    ReplyDelete
  4. I like this improvement over the builder pattern, it might even make people think about the object oriented-ness of the objects they are building as there is a natural relationship between steps and objects

    ReplyDelete
  5. Very clever extension to the builder pattern!

    If I got it right, this basically adds two major benefits to the original pattern:

    - forces the client to set mandatory parameters
    - avoids invariants when only one property within a set of properties must be set

    I have written an eclipse plugin for automatically generating builders (http://mbelow.blogspot.de/2012/09/eclipse-plugin-for-creating-builders.html) and I would love to integrate your ideas if you don't mind.

    ReplyDelete
    Replies
    1. Hey Martin, thanks for the comment.
      Yes, you got it right, with this pattern you can completely guide the user in building the object as it should be.
      Go ahead, develop the plugin and let me know if you need help and when it's finished.

      Marco

      Delete
  6. Hi Marco,

    thanks very much! I just thought of a slight variation of your pattern, which eliminates the need to instantiate a new object for each step. It uses interfaces for the steps, and has one class that implements all step-interfaces. As in your code, the "setter"-methods on the builder return the step-type, exposing only the methods valid for the current step:

    http://pastebin.com/AGd3pTR4

    Any thoughts on this?

    Cheers,
    Martin

    PS: A first version of the eclipse plugin can be found here, but it does not include your extension to the pattern yet.

    ReplyDelete
    Replies
    1. I like it. It cleans the whole steps definition. Well though.

      Delete
    2. I will actually change the implementation of PaninoStepBuilder using your idea!

      Delete
  7. Hi Marco,

    I had implemented a step builder based around inner classes and interfaces, but your implementation is cleaner and would make for easier maintenance of the builder than my effort.

    Good stuff & thanks for sharing
    Paul

    ReplyDelete
    Replies
    1. Thanks Paul,

      This pattern can be implemented with inner classes and no interfaces as well (the original version was like this) and you implement the step logic straight into each inner class.
      Are both valid ways, but I think this is a bit more clean.

      Marco

      Delete
  8. This looks cool! But I have a concern, let's say you have your object and your builder in place, then your object needs to add one more required property, so you have to modify your builder to include a step for that property, and this step should be one of the first steps in your builder call chain -> it will break all the client code of your builder which is used to set some non-required properties (because those steps are usually the last steps in the call chain)
    Any idea on that?

    ReplyDelete
  9. Hey Trương, not sure I understood exactly what you meant, but If you need to add a required property, you should break the client code! This is all the purpose of this pattern. If the property is optional, you just add a method in the step interface and you will not break the client code.

    ReplyDelete
  10. Break client code is not fun :).
    The traditional builder pattern does not have this problem (actually it does have problem, but with Factory Method pattern in place, everything is fine)
    About your arguments:
    >It does not really guide the user through the creation.
    As the client code developer, I really don't care about how to create my object. That purpose of the builder pattern! Abstract the way to construct a "valid" object.
    >A user can always call the build method in any moment, even without the needed information.
    Then you use the builder pattern wrong! Your builder must always create a valid object whenever the "build" method is called.
    > There is no way to guide the user from a creation path instead of another based on conditions.
    You are right at this point.
    >There is always the risk to leave your object in an inconsistent state.
    If you implement the builder pattern correctly, this will never happen.
    >All methods are always available, leaving the responsibility of which to use and when to use to the user who did not write the api.
    Then the user of the api can override any "default" value of all the required properties.

    The "correct" way (from my opinion): for all the required properties, the builder should provide a constructor to take all the values for those properties, but when you add another required property, this will break all the client code. There is one way to fix this. Just make builder constructors inaccessible from client code and wrap around that with a Factory method pattern, something like:
    public class BuilderFactory {
    public DomainObjectBuilder aDomainObject() {
    return new DomainObjectBuilder(defaultRequiredProp1, defaultRequiredProp2)
    }
    }

    So your client code will look like this:
    DomainObject object = BuilderFactory.aDomainObject().withBlah(value1).withBlahblah(value2).build();
    With the help of static import, it will look like this:
    DomainObject object = aDomainObject().withBlah(value1).withBlahblah(value2).build();
    With this approach, when I add another required field, I just modify the BuilderFactory to include the new property through the new constructor of the builder. No need to change the client code for that.
    With your "step" approach, it's not that easy to do an ad-hoc customization for one of required properties. You force the client to call the method that they may not want to.
    It's just my 2 cents though.
    Feedback is welcomed.

    ReplyDelete
  11. "With this approach, when I add another required field, I just modify the BuilderFactory to include the new property through the new constructor of the builder. No need to change the client code for that."

    And what happens when your client wants to set this to a non-default value, they have to add a new withBlahBlah() call

    A client code change. And you have just introduced another layer of obscurity.

    I can just as easily make the change in the domain model from the step builder and set new properties to default values in the intialization of this object from the step builder without changing the client code.

    Simple fact is the client code will have to change to support a non default value.

    ReplyDelete
    Replies
    1. "And what happens when your client wants to set this to a non-default value, they have to add a new withBlahBlah() call"
      As you can see, a client code change was originated from the client need :).

      "I can just as easily make the change in the domain model from the step builder and set new properties to default values in the intialization of this object from the step builder without changing the client code."
      In that case, does your new required property need a "required step" in the call chain? If the answer is yes, you are in a serious problem. You have to change the client code for all the use cases (even the client code does not want to set a non-default value). If the answer is no, then is it a real "required" property? Why do I have to set a default property for a non-required property?

      Delete
  12. You add a required field and the client will not know what is required.
    For example, when I came up with this pattern, I was writing an API that involved the creation of an Object in charge of connecting to a file. This file can be local or remote. In case it's remote the step builder is forcing the client to add "user" and "password" as a required step, in case of local the client does not have the possibility to add "user" and "password".
    Also, you can add default values in the step builder like you do in the normal builder.
    To add a new MANDATORY step you need to release a new version of your API. If the client switch to this version he will need to add the MANDATORY field, simple!
    Anyway, normally you find out all the mandatory fields during design process, but, yes it can happen that you need to add something mandatory at some point(it's very rare) and in this case, if the client wants to upgrade the API, he needs to adapt his code to the new version.
    To finish, with step builder pattern I do not want to complete replace the normal builder, but just offering a different approach to the same problem. If you recon that in your code the normal builder fit better, just use the normal builder.

    ReplyDelete
    Replies
    1. I really thanks for your sharing about this. I do not mean to criticize by any means. I just want to point out some "weakness" of this approach (from my point of view only) and I hope that we can discus to improve the solution as whole.
      You are right, maybe in your use cases, this approach is suitable.

      Delete