Skip to main content
5 of 10
added 208 characters in body
RainningTW
  • 531
  • 4
  • 14

Pizza builder with builder/template/factory pattern

I tried to implement the builder pattern of GoF. After searching for almost every related posts/examples on the Internet, I'm still confused. But I found that there are two kinds of patterns, which are both called builder:

  • Fluent Interface : A famous post, but a comment pointed out it's actually a fluent interface, still useful, though. Also notice that it's not the same as Method Chaining, see the differences.

  • Builder Pattern @thejavageek.com : The author gives a very clear structure of Builder Pattern: Director, Builder, ConcreteBuilder, Product.


Update: I remove the Factory class, and make the Pizza.Builder, not pizza itself, to bake the pizza. The template version now is optional, and you can still override some methods if you want.

For the double cheese problem, I will prefer to use Decorator pattern to solve it.

Main.java

public class Main {
    public static void main(String[] args) throws InterruptedException {
        // Raw build edition.
        Pizza myPizza = new Pizza.Builder(20)
                                 .withBacon()
                                 .withCheese()
                                 .withPepperoni()
                                 .rawBuild();
        // Without standard build, pizza is not baked.
        System.out.println(myPizza);
        
        // You can also customize some method in standard build.
        Pizza.Builder customizer = new Pizza.Builder(20)
                                            .withBacon()
                                            .withCheese()
                                            .withPepperoni();
        myPizza = Pizza.Builder.standardBuild(customizer);
        // Now you have to wait 3 seconds of baking.
        System.out.println(myPizza);
    }
}

Pizza.java

public class Pizza {
    private final int size;
    private final boolean cheese;
    private final boolean pepperoni;
    private final boolean bacon;
    private final boolean baked;
    
    private Pizza(Builder builder) {
        // Validation code is omitted.
        size = builder.size;
        cheese = builder.cheese;
        pepperoni = builder.pepperoni;
        bacon = builder.bacon;
        baked = builder.baked;
    }
    
    public static class Builder {
        private int size;
        private boolean cheese = false;
        private boolean pepperoni = false;
        private boolean bacon = false;
        private boolean baked = false;
        
        private void bakingPizza() throws InterruptedException {
            System.out.println("Baking the pizza...");
            Thread.sleep(3000);
            baked = true;
        }
        
        public static Pizza standardBuild(Builder builder) throws InterruptedException {
            builder.bakingPizza();
            return new Pizza(builder);
        }
        
        public Pizza rawBuild() {
            return new Pizza(this);
        }
        
        public Builder(int size) {
            this.size = size;
        }
        
        public Builder withSize(int size) {
            this.size = size;
            return this;
        }
        
        public Builder withCheese() {
            cheese = true;
            return this;
        }
        
        public Builder withPepperoni() {
            pepperoni = true;
            return this;
        }
        
        public Builder withBacon() {
            bacon = true;
            return this;
        }
    }
    
    public String toString() {
        return String.format("pizza={size=%d, cheese=%s, pepperoni=%s, bacon=%s, baked=%s}",
            size,
            cheese,
            pepperoni,
            bacon,
            baked);
    }
}

Result:

pizza={size=20, cheese=true, pepperoni=true, bacon=true, baked=false}
Baking the pizza...
pizza={size=20, cheese=true, pepperoni=true, bacon=true, baked=true}

End of Update


Finally I decide to make my own version of "Builder Pattern", which follows the first link above, with some modifications:

  • The static method Pizza.makePizza() acts as the Director.
  • For simplicity, I didn't make the Factory a factory method or abstract factory, but it can be without problem.
  • Before you want to make a pizza, you have to override some methods, which you can put anything must be done before the pizza is created.

Main.java

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Factory myFactory = new Factory();
        
        Pizza myPizza = Pizza.makePizza(new Pizza.Builder(myFactory) {
            @Override
            public void prepareDough() {
                myFactory.prepareDough();
            }
            
            @Override
            public void prepareToppings() {
                myFactory.prepareToppings();
            }
        }.withSize(20).withBacon().withPepperoni());
        
        System.out.println(myPizza);
    }
}

Pizza.java

public class Pizza {
    private final int size;
    private final boolean cheese;
    private final boolean pepperoni;
    private final boolean bacon;
    
    private Pizza(Builder builder) {
        size = builder.size;
        cheese = builder.cheese;
        pepperoni = builder.pepperoni;
        bacon = builder.bacon;
    }
    
    public static Pizza makePizza(Builder builder) throws InterruptedException {
        builder.prepareDough();
        Thread.sleep(2000);
        builder.prepareToppings();
        Thread.sleep(2000);
        return new Pizza(builder);
    }
    
    public static abstract class Builder {
        private int size;
        private boolean cheese = false;
        private boolean pepperoni = false;
        private boolean bacon = false;
        
        public Builder(Factory factory) {
        }
        
        public Builder withSize(int size) {
            this.size = size;
            return this;
        }
        
        public Builder withCheese() {
            cheese = true;
            return this;
        }
        
        public Builder withPepperoni() {
            pepperoni = true;
            return this;
        }
        
        public Builder withBacon() {
            bacon = true;
            return this;
        }
        
        protected abstract void prepareDough();
        protected abstract void prepareToppings();
    }
    
    public String toString() {
        return String.format("pizza={size=%d, cheese=%s, pepperoni=%s, bacon=%s}",
            size,
            cheese,
            pepperoni,
            bacon);
    }
}

Factory.java

public class Factory {
    public Factory() {}
    
    public void prepareDough() {
        System.out.println("Preparing dough...");
    }
    
    public void prepareToppings() {
        System.out.println("Preparing toppings...");
    }
}

Result:

Preparing dough...
Preparing toppings...
pizza={size=20, cheese=false, pepperoni=true, bacon=true}
RainningTW
  • 531
  • 4
  • 14