patternjavaMinor
Pizza lover program with Builder Pattern of GoF
Viewed 0 times
pizzabuilderwithprogramlovergofpattern
Problem
I love pizza and design patterns. This is my first program to practice the builder pattern of GoF, without fluent interface. I put emphasis on the structure and characters of builder pattern.
Here are some notes, hope it will help you read:
-
Directors --> MeatPizzaLover, CheesePizzaLover. I add an extra-layer of abstraction, PizzaLover, to them.
-
Builder --> PizzaRecipe.
-
ConcreteBuilder --> CheesePizzaRecipe, MeatPizzaRecipe.
-
ComplexProduct --> Oven, Pizza, PrepareTips.
The PrepareTips is the result of a technique called Simple Factory. It's not a pattern but quite useful since it moves the variation part out of its client, the recipes.
There are a lot of points to improve. If you found anything that causes it not a builder pattern of GoF, put emphasis on it, thanks.
Maybe I should make two specific pizza types, CheesePizza and MeatPizza, but sorry, I didn't. I just want to focus on all the main characters in builder pattern. Hope this will not disturb your reading.
UML:
Main.java
Pizza.java
```
public class Pizza {
private Size size;
private String bacon;
private String cheese;
private String pepperoni;
private List toppings;
public enum Size { NONE, PERSONAL, STANDARD, FAMILY }
// Omit the default constructor
// Setters & getters.
public void setSiz
Here are some notes, hope it will help you read:
-
Directors --> MeatPizzaLover, CheesePizzaLover. I add an extra-layer of abstraction, PizzaLover, to them.
-
Builder --> PizzaRecipe.
-
ConcreteBuilder --> CheesePizzaRecipe, MeatPizzaRecipe.
-
ComplexProduct --> Oven, Pizza, PrepareTips.
The PrepareTips is the result of a technique called Simple Factory. It's not a pattern but quite useful since it moves the variation part out of its client, the recipes.
There are a lot of points to improve. If you found anything that causes it not a builder pattern of GoF, put emphasis on it, thanks.
Maybe I should make two specific pizza types, CheesePizza and MeatPizza, but sorry, I didn't. I just want to focus on all the main characters in builder pattern. Hope this will not disturb your reading.
UML:
Main.java
public class Main {
public static void main(String[] args) throws InterruptedException {
PizzaLover pizzaLover;
Pizza pizza;
pizzaLover = new MeatPizzaLover();
MeatPizzaRecipe meatPizzaRecipe = new MeatPizzaRecipe();
pizzaLover.bakePizza(meatPizzaRecipe);
pizza = meatPizzaRecipe.takeOutPizzaFromOven();
System.out.println(pizza);
pizzaLover = new CheesePizzaLover();
CheesePizzaRecipe cheesePizzaRecipe = new CheesePizzaRecipe();
pizzaLover.bakePizza(cheesePizzaRecipe);
pizza = cheesePizzaRecipe.takeOutPizzaFromOven();
System.out.println(pizza);
}
}Pizza.java
```
public class Pizza {
private Size size;
private String bacon;
private String cheese;
private String pepperoni;
private List toppings;
public enum Size { NONE, PERSONAL, STANDARD, FAMILY }
// Omit the default constructor
// Setters & getters.
public void setSiz
Solution
I think you model the domain wrong.
A recipe is a list of instructions with a list of ingredients. What kind of recipe it is does not matter. The "cheeseyness" of a recipe does not matter for the class.
So I think there should only be a
Consider modeling
Looking more at the diagram, I also think that the current program doesn't work with a user. The user does not get to decide what pizza they want. Pizza lovers are forced to make a pizza they love only to have it taken away. This doesn't make the pizza lover or the user happy.
We'll set out to do the following:
In order to avoid serving customer unfinished pizza's (and to have the whole builder pattern make sense to use, since it's rather hefty), we should make it so that all
Your current
Consider making a
You can reuse your current Pizza class as an
Create a new
I've written parts of a
The goal of a builder class is to hide all/most the object creation logic, which may involve filling defaults, defining buffers etc, from the client.
So, how do you use this builder?
Part of a useful builder is the ability to either tell the builder, or another related object that the builder accepts, what you want from the builder. That's where the
This would be one of your consumers. They're doing... mostly the right stuff, but on too low an abstraction level. I think they're on the wrong side of the counter! All this stretching, adding ingredients, and baking is part of the PizzaBuilder's job!
The PizzaLover, instead, as the client, defines the recipe.
```
//pseud
A recipe is a list of instructions with a list of ingredients. What kind of recipe it is does not matter. The "cheeseyness" of a recipe does not matter for the class.
So I think there should only be a
Recipe class, of which there are two instances, namely a meatLoversPizzaRecipe, and a cheeseLoversPizzaRecipe. Consider modeling
Instruction (as abstract) and Ingredient (as enum?) as well - that way you could make something like an AddToppingInstruction. I don't wanna go to deep with the recipes, so we'll keep it simple on that front.Looking more at the diagram, I also think that the current program doesn't work with a user. The user does not get to decide what pizza they want. Pizza lovers are forced to make a pizza they love only to have it taken away. This doesn't make the pizza lover or the user happy.
We'll set out to do the following:
- Allow the user to decide what pizza they want
- Make proper use of the builder pattern
- Take a look at your existing code
In order to avoid serving customer unfinished pizza's (and to have the whole builder pattern make sense to use, since it's rather hefty), we should make it so that all
Pizza's are built and baked. That is to say, a Pizza is done after instantiation.public class Pizza {
private Size size;
private String bacon;
private String cheese;
private String pepperoni;
private List toppings;Your current
Pizza isn't a finished pizza, it's a pizza that's in a certain state of construction.Consider making a
Pizza that does not have setters, just getters (to examine the Pizza) and action methods (to eat the pizza). You can reuse your current Pizza class as an
UnbakedPizza class.Create a new
PizzaBuilder class which has the job to, well, make Pizza's. They're instantiated with an Oven, and will give Pizza's when handed a Recipe.I've written parts of a
PizzaBuilder here to give you an idea how a Builder class would work.public class PizzaBuilder {
private Oven oven;
//... constructor
public Pizza makePizza(PizzaRecipe recipe) {
//perhaps a recipe needs parsing, you could explode recipe into instructions here
PizzaBase base = makeBase(recipe);
UnbakedPizza rawPizza = applyToppings(recipe, base);
Pizza bakedPizza = cookPizza(rawPizza);
return bakedPizza;//perhaps we could add seasonings to bakedPizza?
}
private PizzaBase makeBase(PizzaRecipe recipe) {
//Maybe you have to convert the recipe to other values here?
return new PizzaBase(recipe.getSize(), recipe.getDoughType());
}
private UnbakedPizza applyToppings(PizzaRecipe recipe, PizzaBase base) {
List toppings = recipe.getToppings();
//special pizza making business logic goes here
return new UnbakedPizza(base, toppings);
}
private Pizza bakePizza(PizzaRecipe recipe, UnbakedPizza rawPizza) {
prepareOven(recipe);//hey look, pizza business logic
oven.add(rawPizza);
//waiting happens
return oven.retrieve();
}
}The goal of a builder class is to hide all/most the object creation logic, which may involve filling defaults, defining buffers etc, from the client.
So, how do you use this builder?
Part of a useful builder is the ability to either tell the builder, or another related object that the builder accepts, what you want from the builder. That's where the
PizzaRecipe comes in. public class MeatPizzaLover extends PizzaLover {
@Override
public void bakePizza(PizzaRecipe pizzaRecipe) throws InterruptedException {
System.out.println("In MeatPizzaLover.bakePizza():");
pizzaRecipe.prepareIngredients();
System.out.println("Set oven temperature to 300.0*F");
pizzaRecipe.setOvenTemperature(300.0);
System.out.println("Heat the oven about 5 seconds...");
pizzaRecipe.setTimeOfOvenTimer(5);
if (pizzaRecipe.getOvenTemperature() < 500.0) {
System.out.println("Since oven temperature less than 500.0*F, re-heat the oven for 2-sec...");
pizzaRecipe.setTimeOfOvenTimer(2);
}
System.out.println("Stretching the pizza dough three times to make it FAMILY size.");
pizzaRecipe.stretchOutDough();
pizzaRecipe.stretchOutDough();
pizzaRecipe.stretchOutDough();
pizzaRecipe.addSauce();
pizzaRecipe.addToppings();
System.out.println("Adding meat twice.");
pizzaRecipe.addMeat();
pizzaRecipe.addMeat();
pizzaRecipe.addCheese();
pizzaRecipe.putPizzaInOven();
}
}This would be one of your consumers. They're doing... mostly the right stuff, but on too low an abstraction level. I think they're on the wrong side of the counter! All this stretching, adding ingredients, and baking is part of the PizzaBuilder's job!
The PizzaLover, instead, as the client, defines the recipe.
```
//pseud
Code Snippets
public class Pizza {
private Size size;
private String bacon;
private String cheese;
private String pepperoni;
private List<String> toppings;public class PizzaBuilder {
private Oven oven;
//... constructor
public Pizza makePizza(PizzaRecipe recipe) {
//perhaps a recipe needs parsing, you could explode recipe into instructions here
PizzaBase base = makeBase(recipe);
UnbakedPizza rawPizza = applyToppings(recipe, base);
Pizza bakedPizza = cookPizza(rawPizza);
return bakedPizza;//perhaps we could add seasonings to bakedPizza?
}
private PizzaBase makeBase(PizzaRecipe recipe) {
//Maybe you have to convert the recipe to other values here?
return new PizzaBase(recipe.getSize(), recipe.getDoughType());
}
private UnbakedPizza applyToppings(PizzaRecipe recipe, PizzaBase base) {
List<String> toppings = recipe.getToppings();
//special pizza making business logic goes here
return new UnbakedPizza(base, toppings);
}
private Pizza bakePizza(PizzaRecipe recipe, UnbakedPizza rawPizza) {
prepareOven(recipe);//hey look, pizza business logic
oven.add(rawPizza);
//waiting happens
return oven.retrieve();
}
}public class MeatPizzaLover extends PizzaLover {
@Override
public void bakePizza(PizzaRecipe pizzaRecipe) throws InterruptedException {
System.out.println("In MeatPizzaLover.bakePizza():");
pizzaRecipe.prepareIngredients();
System.out.println("Set oven temperature to 300.0*F");
pizzaRecipe.setOvenTemperature(300.0);
System.out.println("Heat the oven about 5 seconds...");
pizzaRecipe.setTimeOfOvenTimer(5);
if (pizzaRecipe.getOvenTemperature() < 500.0) {
System.out.println("Since oven temperature less than 500.0*F, re-heat the oven for 2-sec...");
pizzaRecipe.setTimeOfOvenTimer(2);
}
System.out.println("Stretching the pizza dough three times to make it FAMILY size.");
pizzaRecipe.stretchOutDough();
pizzaRecipe.stretchOutDough();
pizzaRecipe.stretchOutDough();
pizzaRecipe.addSauce();
pizzaRecipe.addToppings();
System.out.println("Adding meat twice.");
pizzaRecipe.addMeat();
pizzaRecipe.addMeat();
pizzaRecipe.addCheese();
pizzaRecipe.putPizzaInOven();
}
}//pseudo code
@Override
public Pizza orderPizza() {
PizzaRecipe recipe = new PizzaRecipe();
recipe.setSize(FAMILY);
recipe.addTopping("cheese");
recipe.addTopping("meat");
recipe.setBakeTime(5);
recipe.setTemperature(300);
return Pizzeria.getBaker().bakePizza(recipe);
}public class Pizza {
private Size size;
private String bacon;
private String cheese;
private String pepperoni;
private List<String> toppings;
public enum Size { NONE, PERSONAL, STANDARD, FAMILY }
public int getSize() {
int inch = 10;
switch (this.size) {
case PERSONAL: inch = 10; break;
case STANDARD: inch = 14; break;
case FAMILY: inch = 16; break;
default:
}
return inch;
}Context
StackExchange Code Review Q#160130, answer score: 5
Revisions (0)
No revisions yet.