3
\$\begingroup\$

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:

  • Bloch Builder : 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.


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}
\$\endgroup\$
2
  • 2
    \$\begingroup\$ Please don't change or add to the code in the question after receiving answers. See What should I do when someone answers my question? \$\endgroup\$
    – Phrancis
    Apr 6, 2017 at 2:32
  • 2
    \$\begingroup\$ That would be fine if you want to post your improved code as an answer, if you explain what you improved about it. I wouldn't make that the accepted answer though, that would indeed be rude :) \$\endgroup\$
    – Phrancis
    Apr 6, 2017 at 2:35

2 Answers 2

4
\$\begingroup\$

Your main() code feels cumbersome, and the modelling seems unnatural to me. I also find that .withSize(20).withBacon().withPepperoni() is hard to read, perhaps due to its placement after the definition of the anonymous subclass of Pizza.Builder.

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);
    }
}

Assuming that you are interested in constructing an immutable Pizza, I would prefer to see:

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Pizza myPizza = new PizzaBase(20).addTopping(Pizza.Topping.BACON)
                                         .addTopping(Pizza.Topping.PEPPERONI)
                                         .bake();
        System.out.println(myPizza);
    }
}

In particular:

  • This interface mimics the process of making a pizza.
  • The base is mandatory, and you must specify its size. The toppings are optional.
  • Offering an .addTopping(…) method that accepts a parameter is more flexible. You might even be able to .addTopping(CHEESE) twice to get extra cheese.
  • I'm not convinced that dependency injection for the Factory is worthwhile.
\$\endgroup\$
0
2
\$\begingroup\$

Referent implementation of the Builder.
I do not see Director in your implementation:

public class Director {  
   private Builder builder;   
   public Director(Builder builder) {
     this.builder = builder;
   }  
   public void construct() {        
      builder.createProduct().buildPart1("part1").buildPart2("part2");
 }}
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.