Builder
The builder pattern is useful for avoiding a huge list of constructors for a class. Let's imagine a class that stores the mode of transport (of an employee for instance). Here is the constructor that takes a MetroLine object as a parameter:
modeOfTransport(MetroLine) |
Now we need a constructor for someone who uses the car, the bus or the train:
new modeOfTransport(MetroLine)
new modeOfTransport(Train) |
For some of them, we need to indicate a travel allowance:
new modeOfTransport(MetroLine)
new modeOfTransport(Train, Integer) |
We have employees who use several modes of transport to go to work. However, we realize that the list of constructors is now becoming a mess. Each new parameter leads to an exponential duplication of constructors. If some parameters have the same type, it becomes very confusing. A solution to this issue would be to first build a fake object and then fill it by calling methods on it:
new modeOfTransport(MetroLine)
modeOfTransport.setTravelAllowance(Integer) |
The list of methods is no longer exponential but the state of the object may be inconsistent. A better solution would be to set all the parameters to an object of another class, a pre-constructor, and then pass this object to the constructor of ModeOfTransport:
modeOfTransportPreConstructor.setMetroLine(MetroLine)
new modeOfTransport(ModeOfTransportPreConstructor) |
This solution is even better. We only have a single valid ModeOfTransport object. However, the ModeOfTransport constructor can be called with a half-filled pre-constructor. So the pre-constructor should be a builder and should have a method that returns the ModeOfTransport object. This method will only return an object if the builder has been correctly filled, otherwise it returns null:
modeOfTransportBuilder.setMetroLine(MetroLine)
modeOfTransport := modeOfTransportBuilder.getResult() |
So the solution is to use a builder class. Let's see the structure of the code in an UML class diagram:
- Builder: Abstract interface for creating objects (product).
- Concrete Builder: Provides implementation for Builder. It is an object able to construct other objects. Constructs and assembles parts to build the objects.
The builder pattern can be used for objects that contain flat data (html code, SQL query, X.509 certificate...), that is to say data that can't be easily edited. This type of data can not be edited step by step and must be edited all at once. The best way to construct such an object is to use a builder class.
Examples
In Java, the StringBuffer
and StringBuilder
classes follow the builder design pattern. They are used to build String
objects.
Cost
You can easily decide to use it. At worst, the pattern is useless.
Creation
Starting from a plain old class with a public constructor, implementing the design pattern is not very expensive. You can create the builder class alongside your code. Then you will have to remove the existing constructor calls to use the builder instead. The refactoring is hardly automatic.
Maintenance
This design pattern has only small drawbacks. It may lead to small redundancies between the class and the builder structures but the both class have usually different structures. However, it is expensive to convert to an abstract factory.
Removal
The refactoring is hardly automatic. It should be done manually.
Advices
- Put the builder term in the name of the builder class to indicate the use of the pattern to the other developers.
- Build your builder class as a fluent interface. It would increase the pattern interest.
- If the target class contains flat data, your builder class can be constructed as a Composite that implements the Interpreter pattern.
Implementations
Building a car.
/**
* Can have GPS, trip computer and a various number of seaters. Can be a city car, a sport car or a cabriolet.
*/
public class Car {
/**
* The description of the car.
*/
private String description = null;
/**
* Construct and return the car.
* @param aDescription The description of the car.
*/
public Car(String aDescription) {
description = aDescription;
}
/**
* Print the car.
* @return A flat representation.
*/
public String toString() {
return description;
}
}
/**
*
*/
public class CarBuilder {
/**
* Sport car.
*/
private static final int SPORT_CAR = 1;
/**
* City car.
*/
private static final int CITY_CAR = 2;
/**
* Cabriolet.
*/
private static final int CABRIOLET = 3;
/**
* The type of the car.
*/
private int carType;
/**
* True if the car has a trip computer.
*/
private boolean hasTripComputer;
/**
* True if the car has a GPS.
*/
private boolean hasGPS;
/**
* The number of seaters the car may have.
*/
private int seaterNumber;
/**
* Construct and return the car.
* @return a Car with the right options.
*/
public Car getResult() {
return new Car((carType == CITY_CAR) ? "A city car" : ((carType == SPORT_CAR) ? "A sport car" : "A cabriolet")
+ " with " + seaterNumber + " seaters"
+ (hasTripComputer ? " with a trip computer" : "")
+ (hasGPS ? " with a GPS" : "")
+ ".");
}
/**
* Tell the builder the number of seaters.
* @param number the number of seaters the car may have.
*/
public void setSeaters(int number) {
seaterNumber = number;
}
/**
* Make the builder remember that the car is a city car.
*/
public void setCityCar() {
carType = CITY_CAR;
}
/**
* Make the builder remember that the car is a cabriolet.
*/
public void setCabriolet() {
carType = CABRIOLET;
}
/**
* Make the builder remember that the car is a sport car.
*/
public void setSportCar() {
carType = SPORT_CAR;
}
/**
* Make the builder remember that the car has a trip computer.
*/
public void setTripComputer() {
hasTripComputer = true;
}
/**
* Make the builder remember that the car has not a trip computer.
*/
public void unsetTripComputer() {
hasTripComputer = false;
}
/**
* Make the builder remember that the car has a global positioning system.
*/
public void setGPS() {
hasGPS = true;
}
/**
* Make the builder remember that the car has not a global positioning system.
*/
public void unsetGPS() {
hasGPS = false;
}
}
/**
* Construct a CarBuilder called carBuilder and build a car.
*/
public class Director {
public static void main(String[] args) {
CarBuilder carBuilder = new CarBuilder();
carBuilder.setSeaters(2);
carBuilder.setSportCar();
carBuilder.setTripComputer();
carBuilder.unsetGPS();
Car car = carBuilder.getResult();
System.out.println(car);
}
}
It will produce:
A sport car with 2 seaters with a trip computer.
Building a pizza.
/** "Product" */
class Pizza {
private String dough = "";
private String sauce = "";
private String topping = "";
public void setDough(final String dough) {
this.dough = dough;
}
public void setSauce(final String sauce) {
this.sauce = sauce;
}
public void setTopping(final String topping) {
this.topping = topping;
}
}
/** "Abstract Builder" */
abstract class PizzaBuilder {
protected Pizza pizza;
public abstract void buildDough();
public abstract void buildSauce();
public abstract void buildTopping();
public void createNewPizzaProduct() {
this.pizza = new Pizza();
}
public Pizza getPizza() {
return this.pizza;
}
}
/** "ConcreteBuilder" */
class HawaiianPizzaBuilder extends PizzaBuilder {
@Override public void buildDough() {
this.pizza.setDough("cross");
}
@Override public void buildSauce() {
this.pizza.setSauce("mild");
}
@Override public void buildTopping() {
this.pizza.setTopping("ham+pineapple");
}
}
/** "ConcreteBuilder" */
class SpicyPizzaBuilder extends PizzaBuilder {
@Override public void buildDough() {
this.pizza.setDough("pan baked");
}
@Override public void buildSauce() {
this.pizza.setSauce("hot");
}
@Override public void buildTopping() {
this.pizza.setTopping("pepperoni+salami");
}
}
/** "Director" */
class Waiter {
private PizzaBuilder pizzaBuilder;
public void setPizzaBuilder(final PizzaBuilder pb) {
this.pizzaBuilder = pb;
}
public Pizza getPizza() {
return this.pizzaBuilder.getPizza();
}
public void constructPizza() {
this.pizzaBuilder.createNewPizzaProduct();
this.pizzaBuilder.buildDough();
this.pizzaBuilder.buildSauce();
this.pizzaBuilder.buildTopping();
}
}
/** A customer ordering a pizza. */
class BuilderExample {
public static void main(final String[] args) {
final Waiter waiter = new Waiter();
final PizzaBuilder hawaiianPizzaBuilder = new HawaiianPizzaBuilder();
final PizzaBuilder spicyPizzaBuilder = new SpicyPizzaBuilder();
waiter.setPizzaBuilder(hawaiianPizzaBuilder);
waiter.constructPizza();
final Pizza pizza = waiter.getPizza();
waiter.setPizzaBuilder(spicyPizzaBuilder);
waiter.constructPizza();
final Pizza anotherPizza = waiter.getPizza();
}
}
The Director assembles a bicycle instance in the example above, delegating the construction to a separate builder object that has been given to the Director by the Client.
/// <summary>
/// Represents a product created by the builder.
/// </summary>
public class Bicycle
{
public Bicycle(string make, string model, string colour, int height)
{
Make = make;
Model = model;
Colour = colour;
Height = height;
}
public string Make { get; set; }
public string Model { get; set; }
public int Height { get; set; }
public string Colour { get; set; }
}
/// <summary>
/// The builder abstraction.
/// </summary>
public interface IBicycleBuilder
{
Bicycle GetResult();
string Colour { get; set; }
int Height { get; set; }
}
/// <summary>
/// Concrete builder implementation.
/// </summary>
public class GTBuilder : IBicycleBuilder
{
public Bicycle GetResult()
{
return Height == 29 ? new Bicycle("GT", "Avalanche", Colour, Height) : null;
}
public string Colour { get; set; }
public int Height { get; set; }
}
/// <summary>
/// The director.
/// </summary>
public class MountainBikeBuildDirector
{
private IBicycleBuilder _builder;
public MountainBikeBuildDirector(IBicycleBuilder builder)
{
_builder = builder;
}
public void Construct()
{
_builder.Colour = "Red";
_builder.Height = 29;
}
public Bicycle GetResult()
{
return this._builder.GetResult();
}
}
public class Client
{
public void DoSomethingWithBicycles()
{
var director = new MountainBikeBuildDirector(new GTBuilder());
// Director controls the stepwise creation of product and returns the result.
director.Construct();
Bicycle myMountainBike = director.GetResult();
}
}
Another example:
using System;
namespace BuilderPattern
{
// Builder - abstract interface for creating objects (the product, in this case)
abstract class PizzaBuilder
{
protected Pizza pizza;
public Pizza GetPizza()
{
return pizza;
}
public void CreateNewPizzaProduct()
{
pizza = new Pizza();
}
public abstract void BuildDough();
public abstract void BuildSauce();
public abstract void BuildTopping();
}
// Concrete Builder - provides implementation for Builder; an object able to construct other objects.
// Constructs and assembles parts to build the objects
class HawaiianPizzaBuilder : PizzaBuilder
{
public override void BuildDough()
{
pizza.dough = "cross";
}
public override void BuildSauce()
{
pizza.sauce = "mild";
}
public override void BuildTopping()
{
pizza.topping = "ham+pineapple";
}
}
// Concrete Builder - provides implementation for Builder; an object able to construct other objects.
// Constructs and assembles parts to build the objects
class SpicyPizzaBuilder : PizzaBuilder
{
public override void BuildDough()
{
pizza.dough = "pan baked";
}
public override void BuildSauce()
{
pizza.sauce = "hot";
}
public override void BuildTopping()
{
pizza.topping = "pepperoni + salami";
}
}
// Director - responsible for managing the correct sequence of object creation.
// Receives a Concrete Builder as a parameter and executes the necessary operations on it.
class Cook
{
private PizzaBuilder _pizzaBuilder;
public void SetPizzaBuilder(PizzaBuilder pb)
{
_pizzaBuilder = pb;
}
public Pizza GetPizza()
{
return _pizzaBuilder.GetPizza();
}
public void ConstructPizza()
{
_pizzaBuilder.CreateNewPizzaProduct();
_pizzaBuilder.BuildDough();
_pizzaBuilder.BuildSauce();
_pizzaBuilder.BuildTopping();
}
}
// Product - The final object that will be created by the Director using Builder
public class Pizza
{
public string dough = string.Empty;
public string sauce = string.Empty;
public string topping = string.Empty;
}
class Program
{
static void Main(string[] args)
{
PizzaBuilder hawaiianPizzaBuilder = new HawaiianPizzaBuilder();
Cook cook = new Cook();
cook.SetPizzaBuilder(hawaiianPizzaBuilder);
cook.ConstructPizza();
// create the product
Pizza hawaiian = cook.GetPizza();
PizzaBuilder spicyPizzaBuilder = new SpicyPizzaBuilder();
cook.SetPizzaBuilder(spicyPizzaBuilder);
cook.ConstructPizza();
// create another product
Pizza spicy = cook.GetPizza();
}
}
}
This C++11 implementation is based on the pre C++98 implementation in the book.
#include <iostream>
enum Direction {North, South, East, West};
class MapSite {
public:
virtual void enter() = 0;
virtual ~MapSite() = default;
};
class Room : public MapSite {
public:
Room() :roomNumber(0) {}
Room(int n) :roomNumber(n) {}
void setSide(Direction d, MapSite* ms) {
std::cout << "Room::setSide " << d << ' ' << ms << '\n';
}
virtual void enter() {}
Room(const Room&) = delete; // rule of three
Room& operator=(const Room&) = delete;
private:
int roomNumber;
};
class Wall : public MapSite {
public:
Wall() {}
virtual void enter() {}
};
class Door : public MapSite {
public:
Door(Room* r1 = nullptr, Room* r2 = nullptr)
:room1(r1), room2(r2) {}
virtual void enter() {}
Door(const Door&) = delete; // rule of three
Door& operator=(const Door&) = delete;
private:
Room* room1;
Room* room2;
};
class Maze {
public:
void addRoom(Room* r) {
std::cout << "Maze::addRoom " << r << '\n';
}
Room* roomNo(int) const {
return nullptr;
}
};
class MazeBuilder {
public:
virtual ~MazeBuilder() = default;
virtual void buildMaze() = 0;
virtual void buildRoom(int room) = 0;
virtual void buildDoor(int roomFrom, int roomTo) = 0;
virtual Maze* getMaze() {
return nullptr;
}
protected:
MazeBuilder() = default;
};
// If createMaze is passed an object that can create a new maze in its entirety using operations for adding rooms, doors, and walls to the maze it builds, then you can use inheritance to change parts of the maze or the way the maze is built. This is an example of the Builder (110) pattern.
class MazeGame {
public:
Maze* createMaze(MazeBuilder& builder) {
builder.buildMaze();
builder.buildRoom(1);
builder.buildRoom(2);
builder.buildDoor(1, 2);
return builder.getMaze();
}
Maze* CreateComplexMaze(MazeBuilder& builder) {
builder.buildRoom(1);
// ...
builder.buildRoom(1001);
return builder.getMaze();
}
};
class StandardMazeBuilder : public MazeBuilder {
public:
StandardMazeBuilder() :currentMaze(nullptr) {}
virtual void buildMaze() {
currentMaze = new Maze;
}
virtual void buildRoom(int n) {
if (!currentMaze->roomNo(n)) {
Room* room = new Room(n);
currentMaze->addRoom(room);
room->setSide(North, new Wall);
room->setSide(South, new Wall);
room->setSide(East, new Wall);
room->setSide(West, new Wall);
}
}
virtual void buildDoor(int n1, int n2) {
Room* r1 = currentMaze->roomNo(n1);
Room* r2 = currentMaze->roomNo(n2);
Door* d = new Door(r1, r2);
r1->setSide(commonWall(r1,r2), d);
r2->setSide(commonWall(r2,r1), d);
}
virtual Maze* getMaze() {
return currentMaze;
}
StandardMazeBuilder(const StandardMazeBuilder&) = delete; // rule of three
StandardMazeBuilder& operator=(const StandardMazeBuilder&) = delete;
private:
Direction commonWall(Room*, Room*) {
return North;
}
Maze* currentMaze;
};
int main() {
MazeGame game;
StandardMazeBuilder builder;
game.createMaze(builder);
builder.getMaze();
}
The program output is like:
Maze::addRoom 0x2145ed0
Room::setSide 0 0x2146300
Room::setSide 1 0x2146320
Room::setSide 2 0x2146340
Room::setSide 3 0x2146360
Maze::addRoom 0x2146380
Room::setSide 0 0x21463a0
Room::setSide 1 0x21463c0
Room::setSide 2 0x21463e0
Room::setSide 3 0x2146400
Room::setSide 0 0x2146420
Room::setSide 0 0x2146420
Another example:
#include <string>
#include <iostream>
using namespace std;
// "Product"
class Pizza {
public:
void dough(const string& dough) {
dough_ = dough;
}
void sauce(const string& sauce) {
sauce_ = sauce;
}
void topping(const string& topping) {
topping_ = topping;
}
void open() const {
cout << "Pizza with " << dough_ << " dough, " << sauce_ << " sauce and "
<< topping_ << " topping. Mmm." << endl;
}
private:
string dough_;
string sauce_;
string topping_;
};
// "Abstract Builder"
class PizzaBuilder {
public:
// Chinmay Mandal : This default constructor may not be required here
PizzaBuilder()
{
// Chinmay Mandal : Wrong syntax
// pizza_ = new Pizza;
}
const Pizza& pizza() {
return pizza_;
}
virtual void buildDough() = 0;
virtual void buildSauce() = 0;
virtual void buildTopping() = 0;
protected:
Pizza pizza_;
};
class HawaiianPizzaBuilder : public PizzaBuilder {
public:
void buildDough() {
pizza_.dough("cross");
}
void buildSauce() {
pizza_.sauce("mild");
}
void buildTopping() {
pizza_.topping("ham+pineapple");
}
};
class SpicyPizzaBuilder : public PizzaBuilder {
public:
void buildDough() {
pizza_.dough("pan baked");
}
void buildSauce() {
pizza_.sauce("hot");
}
void buildTopping() {
pizza_.topping("pepperoni+salami");
}
};
class Cook {
public:
Cook()
: pizzaBuilder_(NULL/*nullptr*/)//Chinmay Mandal : nullptr replaced with NULL.
{ }
~Cook() {
if (pizzaBuilder_)
delete pizzaBuilder_;
}
void pizzaBuilder(PizzaBuilder* pizzaBuilder) {
if (pizzaBuilder_)
delete pizzaBuilder_;
pizzaBuilder_ = pizzaBuilder;
}
const Pizza& getPizza() {
return pizzaBuilder_->pizza();
}
void constructPizza() {
pizzaBuilder_->buildDough();
pizzaBuilder_->buildSauce();
pizzaBuilder_->buildTopping();
}
private:
PizzaBuilder* pizzaBuilder_;
};
int main() {
Cook cook;
cook.pizzaBuilder(new HawaiianPizzaBuilder);
cook.constructPizza();
Pizza hawaiian = cook.getPizza();
hawaiian.open();
cook.pizzaBuilder(new SpicyPizzaBuilder);
cook.constructPizza();
Pizza spicy = cook.getPizza();
spicy.open();
}
<?php
/** "Product" */
class Pizza {
protected $dough;
protected $sauce;
protected $topping;
public function setDough($dough) {
$this->dough = $dough;
}
public function setSauce($sauce) {
$this->sauce = $sauce;
}
public function setTopping($topping) {
$this->topping = $topping;
}
public function showIngredients() {
echo "Dough : ".$this->dough."<br/>";
echo "Sauce : ".$this->sauce."<br/>";
echo "Topping : ".$this->topping."<br/>";
}
}
/** "Abstract Builder" */
abstract class PizzaBuilder {
protected $pizza;
public function getPizza() {
return $this->pizza;
}
public function createNewPizzaProduct() {
$this->pizza = new Pizza();
}
public abstract function buildDough();
public abstract function buildSauce();
public abstract function buildTopping();
}
/** "ConcreteBuilder" */
class HawaiianPizzaBuilder extends PizzaBuilder {
public function buildDough() {
$this->pizza->setDough("cross");
}
public function buildSauce() {
$this->pizza->setSauce("mild");
}
public function buildTopping() {
$this->pizza->setTopping("ham + pineapple");
}
}
/** "ConcreteBuilder" */
class SpicyPizzaBuilder extends PizzaBuilder {
public function buildDough() {
$this->pizza->setDough("pan baked");
}
public function buildSauce() {
$this->pizza->setSauce("hot");
}
public function buildTopping() {
$this->pizza->setTopping("pepperoni + salami");
}
}
/** "Director" */
class Waiter {
protected $pizzaBuilder;
public function setPizzaBuilder(PizzaBuilder $pizzaBuilder) {
$this->pizzaBuilder = $pizzaBuilder;
}
public function getPizza() {
return $this->pizzaBuilder->getPizza();
}
public function constructPizza() {
$this->pizzaBuilder->createNewPizzaProduct();
$this->pizzaBuilder->buildDough();
$this->pizzaBuilder->buildSauce();
$this->pizzaBuilder->buildTopping();
}
}
class Tester {
public static function main() {
$oWaiter = new Waiter();
$oHawaiianPizzaBuilder = new HawaiianPizzaBuilder();
$oSpicyPizzaBuilder = new SpicyPizzaBuilder();
$oWaiter->setPizzaBuilder($oHawaiianPizzaBuilder);
$oWaiter->constructPizza();
$pizza = $oWaiter->getPizza();
$pizza->showIngredients();
echo "<br/>";
$oWaiter->setPizzaBuilder($oSpicyPizzaBuilder);
$oWaiter->constructPizza();
$pizza = $oWaiter->getPizza();
$pizza->showIngredients();
}
}
Tester::main();
output :
Dough : cross Sauce : mild Topping : ham + pineapple
Dough : pan baked Sauce : hot Topping : pepperoni + salami
# Product
class Pizza
attr_accessor :dough, :sauce, :topping
end
# Abstract Builder
class PizzaBuilder
def get_pizza
@pizza
end
def create_new_pizza_product
@pizza = Pizza.new
end
def build_dough; end
def build_sauce; end
def build_topping; end
end
# ConcreteBuilder
class HawaiianPizzaBuilder < PizzaBuilder
def build_dough
@pizza.dough = 'cross'
end
def build_sauce
@pizza.sauce = 'mild'
end
def build_topping
@pizza.topping = 'ham+pineapple'
end
end
# ConcreteBuilder
class SpicyPizzaBuilder < PizzaBuilder
def build_dough
@pizza.dough = 'pan baked'
end
def build_sauce
@pizza.sauce = 'hot'
end
def build_topping
@pizza.topping = 'pepperoni+salami'
end
end
# Director
class Waiter
attr_accessor :pizza_builder
def get_pizza
pizza_builder.get_pizza
end
def construct_pizza
pizza_builder.create_new_pizza_product
pizza_builder.build_dough
pizza_builder.build_sauce
pizza_builder.build_topping
end
end
# A customer ordering a pizza.
class BuilderExample
def main(args = [])
waiter = Waiter.new
hawaiian_pizza_builder = HawaiianPizzaBuilder.new
spicy_pizza_builder = SpicyPizzaBuilder.new
waiter.pizza_builder = hawaiian_pizza_builder
waiter.construct_pizza
pizza = waiter.get_pizza
end
end
puts BuilderExample.new.main.inspect
class Animal:
"""
Abstract Animal
"""
legs = 0
tail = False
roar = ''
class Mutator:
"""
Mutator
"""
def mutate(self):
self.animal = Animal()
class Cat(Mutator):
"""
Cat
"""
def create_legs(self):
self.animal.legs = 4
def create_tail(self):
self.animal.tail = True
def create_roar(self):
self.animal.roar = 'meowww!'
class Dog(Mutator):
"""
Dog
"""
def create_legs(self):
self.animal.legs = 4
def create_tail(self):
self.animal.tail = True
def create_roar(self):
self.animal.roar = 'woffff!'
class AnimalOwner:
"""
Farm owner
"""
__mutator = ''
def set_animal(self, mutator):
self.__mutator = mutator
def create_an_animal_for_me(self):
self.__mutator.mutate()
self.__mutator.create_legs()
self.__mutator.create_tail()
self.__mutator.create_roar()
def get_animal(self):
return self.__mutator.animal
c = Cat()
d = Dog()
ao = AnimalOwner()
ao.set_animal(c)
ao.create_an_animal_for_me()
animal = ao.get_animal()
print(animal.roar) # meowww!
program Builder;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
(*Product - final object that will be created by Director using Builder*)
TPizza = class
fDough: string;
fSauce: string;
fTopping: string;
procedure Open;
end;
(*Builder - abstract class as interface for creating objects -> product in this case*)
TPizzaBuilder = class abstract
protected
fPizza: TPizza;
public
function GetPizza: TPizza;
procedure CreateNewPizzaProduct;
procedure BuildDough; virtual; abstract;
procedure BuildSauce; virtual; abstract;
procedure BuildTopping; virtual; abstract;
end;
(*concrete builder - provides implementation for Builder *)
(*an object able to construct other objects.*)
THawaiianPizzaBuilder = class(TPizzaBuilder)
public
procedure BuildDough; override;
procedure BuildSauce; override;
procedure BuildTopping; override;
end;
(*concrete builder = provides implenentation for Builder*)
(*an object able to construct other objects.*)
TSpicyPizzaBuilder = class(TPizzaBuilder)
public
procedure BuildDough; override;
procedure BuildSauce; override;
procedure BuildTopping; override;
end;
(*Director - responsible for managing the correct sequence of object creation.*)
(*Receives a Concrete Builder as a parameter and executes the necessary operations on it.*)
TCook = class
protected
fPizzaBuilder: TPizzaBuilder;
public
procedure SetPizzaBuilder(pb: TPizzaBuilder);
function GetPizza: TPizza;
procedure ConstructPizza;
end;
{ THawaiianPizzaBuilder }
procedure THawaiianPizzaBuilder.BuildDough;
begin
fPizza.fDough:= 'cross';
end;
procedure THawaiianPizzaBuilder.BuildSauce;
begin
fPizza.fSauce:= 'mild';
end;
procedure THawaiianPizzaBuilder.BuildTopping;
begin
fPizza.fTopping:= 'ham + pineapple';
end;
{ TSpicyPizzaBuilder }
procedure TSpicyPizzaBuilder.BuildDough;
begin
fPizza.fDough:= 'pan baked';
end;
procedure TSpicyPizzaBuilder.BuildSauce;
begin
fPizza.fSauce:= 'hot';
end;
procedure TSpicyPizzaBuilder.BuildTopping;
begin
fPizza.fTopping:= 'pepperoni + salami';
end;
{ TCook }
procedure TCook.ConstructPizza;
begin
fPizzaBuilder.CreateNewPizzaProduct;
fPizzaBuilder.BuildDough;
fPizzaBuilder.BuildSauce;
fPizzaBuilder.BuildTopping;
end;
function TCook.GetPizza: TPizza;
begin
Result:= fPizzaBuilder.GetPizza;
end;
procedure TCook.SetPizzaBuilder(pb: TPizzaBuilder);
begin
fPizzaBuilder:= pb;
end;
{ TPizzaBuilder }
procedure TPizzaBuilder.CreateNewPizzaProduct;
begin
fPizza:= TPizza.Create;
end;
function TPizzaBuilder.GetPizza: TPizza;
begin
Result:= fPizza;
end;
{ TPizza }
procedure TPizza.Open;
begin
WriteLn('Pizza with: ' + fDough + ' dough, ' +
fSauce +' souce and '+fTopping +' topping. Mmm.');
end;
var
cook: TCook;
hawaiian, spicy: TPizza;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
cook:= TCook.Create;
cook.SetPizzaBuilder(THawaiianPizzaBuilder.Create);
cook.ConstructPizza;
WriteLn('(* create the product *)');
hawaiian:= cook.GetPizza;
hawaiian.Open;
WriteLn(#13#10+'(* create another product *)');
cook.SetPizzaBuilder(TSpicyPizzaBuilder.Create);
cook.ConstructPizza;
spicy:= cook.GetPizza;
spicy.Open;
WriteLn(#13#10+'Press any key to continue...');
ReadLn;
spicy.Free;
hawaiian.Free;
cook.Free;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.