Builder

From Wikibooks, open books for an open world
Jump to: navigation, search

Abstract Factory Computer Science Design Patterns
Builder
Factory method

The builder pattern is useful to avoid a huge list of constructors for a class. Let's imagine a class that store the mode of transport (of an employee for instance). Here is the constructor that takes a MetroLine object as parameter:

modeOfTransport(MetroLine)

Now we need a constructor for the one that uses the car, the bus or the train:

new modeOfTransport(MetroLine)

new modeOfTransport()
new modeOfTransport(BusLine)

new modeOfTransport(Train)

For some of them, we need to indicate a travel allowance:

new modeOfTransport(MetroLine)

new modeOfTransport(MetroLine, Integer)
new modeOfTransport()
new modeOfTransport(Integer)
new modeOfTransport(BusLine)
new modeOfTransport(BusLine, Integer)
new modeOfTransport(Train)

new modeOfTransport(Train, Integer)

We have employees that have several modes of transport to go to work. We realize that the list of constructors is coming to a mess. Each new parameter leads to an exponent 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 calling methods on it:

new modeOfTransport(MetroLine)

modeOfTransport.setMetroLine(MetroLine)
modeOfTransport.setCarTravel()
modeOfTransport.setBusLine(BusLine)
modeOfTransport.setTrain(Train)

modeOfTransport.setTravelAllowance(Integer)

The list of methods is no more exponent 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)

modeOfTransportPreConstructor.setCarTravel()
modeOfTransportPreConstructor.setBusLine(BusLine)
modeOfTransportPreConstructor.setTrain(Train)
modeOfTransportPreConstructor.setTravelAllowance(Integer)

new modeOfTransport(ModeOfTransportPreConstructor)

This solution is even better. We only have a 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)

modeOfTransportBuilder.setCarTravel()
modeOfTransportBuilder.setBusLine(BusLine)
modeOfTransportBuilder.setTrain(Train)
modeOfTransportBuilder.setTravelAllowance(Integer)

modeOfTransport := modeOfTransportBuilder.getResult()

So the solution is to use a builder class. Let's see the structure of the code on the UML class diagram:

Builder Structure
  • 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 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 aside 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 evolve 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

Implementation in Java

Building a car.

  1. /**
    
  2.  * Can have GPS, trip computer and a various number of seaters. Can be a city car, a sport car or a cabriolet.
    
  3.  */
    
  4. public class Car {
    
  5.   /**
    
  6.    * The description of the car.
    
  7.    */
    
  8.   private String description = null;
    
  9.  
    
  10.   /**
    
  11.    * Construct and return the car.
    
  12.    * @param aDescription The description of the car.
    
  13.    */
    
  14.   public Car(String aDescription) {
    
  15.       description = aDescription;
    
  16.     }
    
  17.  
    
  18.   /**
    
  19.    * Print the car.
    
  20.    * @return A flat representation.
    
  21.    */
    
  22.   public String toString() {
    
  23.       return description;
    
  24.     }
    
  25. }
    
  1. /**
    
  2.  *
    
  3.  */
    
  4. public class CarBuilder {
    
  5.   /**
    
  6.    * Sport car.
    
  7.    */
    
  8.   private static final int SPORT_CAR = 1;
    
  9.  
    
  10.   /**
    
  11.    * City car.
    
  12.    */
    
  13.   private static final int CITY_CAR = 2;
    
  14.  
    
  15.   /**
    
  16.    * Cabriolet.
    
  17.    */
    
  18.   private static final int CABRIOLET = 3;
    
  19.  
    
  20.   /**
    
  21.    * The type of the car.
    
  22.    */
    
  23.   private int carType;
    
  24.  
    
  25.   /**
    
  26.    * True if the car has a trip computer.
    
  27.    */
    
  28.   private boolean hasTripComputer;
    
  29.  
    
  30.   /**
    
  31.    * True if the car has a GPS.
    
  32.    */
    
  33.   private boolean hasGPS;
    
  34.  
    
  35.   /**
    
  36.    * The number of seaters the car may have.
    
  37.    */
    
  38.   private int seaterNumber;
    
  39.  
    
  40.   /**
    
  41.    * Construct and return the car.
    
  42.    * @return a Car with the right options.
    
  43.    */
    
  44.   public Car getResult() {
    
  45.       return new Car((carType == CITY_CAR) ? "A city car" : ((carType == SPORT_CAR) ? "A sport car" : "A cabriolet")
    
  46.         + " with " + seaterNumber + " seaters"
    
  47.         + (hasTripComputer ? " with a trip computer" : "")
    
  48.         + (hasGPS ? " with a GPS" : "")
    
  49.         + ".");
    
  50.     }
    
  51.  
    
  52.   /**
    
  53.    * Tell the builder the number of seaters.
    
  54.    * @param number the number of seaters the car may have.
    
  55.    */
    
  56.   public void setSeaters(int number) {
    
  57.       seaterNumber = number;
    
  58.     }
    
  59.  
    
  60.   /**
    
  61.    * Make the builder remember that the car is a city car.
    
  62.    */
    
  63.   public void setCityCar() {
    
  64.       carType = CITY_CAR;
    
  65.     }
    
  66.  
    
  67.   /**
    
  68.    * Make the builder remember that the car is a cabriolet.
    
  69.    */
    
  70.   public void setCabriolet() {
    
  71.       carType = CABRIOLET;
    
  72.     }
    
  73.  
    
  74.   /**
    
  75.    * Make the builder remember that the car is a sport car.
    
  76.    */
    
  77.   public void setSportCar() {
    
  78.       carType = SPORT_CAR;
    
  79.     }
    
  80.  
    
  81.   /**
    
  82.    * Make the builder remember that the car has a trip computer.
    
  83.    */
    
  84.   public void setTripComputer() {
    
  85.       hasTripComputer = true;
    
  86.     }
    
  87.  
    
  88.   /**
    
  89.    * Make the builder remember that the car has not a trip computer.
    
  90.    */
    
  91.   public void unsetTripComputer() {
    
  92.       hasTripComputer = false;
    
  93.     }
    
  94.  
    
  95.   /**
    
  96.    * Make the builder remember that the car has a global positioning system.
    
  97.    */
    
  98.   public void setGPS() {
    
  99.       hasGPS = true;
    
  100.     }
    
  101.  
    
  102.   /**
    
  103.    * Make the builder remember that the car has not a global positioning system.
    
  104.    */
    
  105.   public void unsetGPS() {
    
  106.       hasGPS = false;
    
  107.     }
    
  108. }
    
  1. /**
    
  2.  * Construct a CarBuilder called carBuilder and build a car.
    
  3.  */
    
  4. public class Director {
    
  5.   public static void main(String[] args) {
    
  6.     CarBuilder carBuilder = new CarBuilder();
    
  7.     carBuilder.setSeaters(2);
    
  8.     carBuilder.setSportCar();
    
  9.     carBuilder.setTripComputer();
    
  10.     carBuilder.unsetGPS();
    
  11.     Car car = carBuilder.getResult();
    
  12.  
    
  13.     System.out.println(car);
    
  14.   }
    
  15. }
    

It will produce:

A sport car with 2 seaters with a trip computer.


Building a pizza.

  1.  /** "Product" */
    
  2. class Pizza {
    
  3.  private String dough = "";
    
  4.  private String sauce = "";
    
  5.  private String topping = "";
    
  6.  
    
  7.  public void setDough(final String dough) {
    
  8.   this.dough = dough;
    
  9.  }
    
  10.  
    
  11.  public void setSauce(final String sauce) {
    
  12.   this.sauce = sauce;
    
  13.  }
    
  14.  
    
  15.  public void setTopping(final String topping) {
    
  16.   this.topping = topping;
    
  17.  }
    
  18. }
    
  1.  /** "Abstract Builder" */
    
  2. abstract class PizzaBuilder {
    
  3.  protected Pizza pizza;
    
  4.  
    
  5.  public abstract void buildDough();
    
  6.  public abstract void buildSauce();
    
  7.  public abstract void buildTopping();
    
  8.  
    
  9.  public void createNewPizzaProduct() {
    
  10.   this.pizza = new Pizza();
    
  11.  }
    
  12.  
    
  13.  public Pizza getPizza() {
    
  14.   return this.pizza;
    
  15.  }
    
  16. }
    
  1.  /** "ConcreteBuilder" */
    
  2. class HawaiianPizzaBuilder extends PizzaBuilder {
    
  3.  @Override public void buildDough() {
    
  4.   this.pizza.setDough("cross");
    
  5.  }
    
  6.  
    
  7.  @Override public void buildSauce() {
    
  8.   this.pizza.setSauce("mild");
    
  9.  }
    
  10.  
    
  11.  @Override public void buildTopping() {
    
  12.   this.pizza.setTopping("ham+pineapple");
    
  13.  }
    
  14. }
    
  1.  /** "ConcreteBuilder" */
    
  2. class SpicyPizzaBuilder extends PizzaBuilder {
    
  3.  @Override public void buildDough() {
    
  4.   this.pizza.setDough("pan baked");
    
  5.  }
    
  6.  
    
  7.  @Override public void buildSauce() {
    
  8.   this.pizza.setSauce("hot");
    
  9.  }
    
  10.  
    
  11.  @Override public void buildTopping() {
    
  12.   this.pizza.setTopping("pepperoni+salami");
    
  13.  }
    
  14. }
    
  1.  /** "Director" */
    
  2. class Waiter {
    
  3.  private PizzaBuilder pizzaBuilder;
    
  4.  
    
  5.  public void setPizzaBuilder(final PizzaBuilder pb) {
    
  6.   this.pizzaBuilder = pb;
    
  7.  }
    
  8.  
    
  9.  public Pizza getPizza() {
    
  10.   return this.pizzaBuilder.getPizza();
    
  11.  }
    
  12.  
    
  13.  public void constructPizza() {
    
  14.   this.pizzaBuilder.createNewPizzaProduct();
    
  15.   this.pizzaBuilder.buildDough();
    
  16.   this.pizzaBuilder.buildSauce();
    
  17.   this.pizzaBuilder.buildTopping();
    
  18.  }
    
  19. }
    
  1.  /** A customer ordering a pizza. */
    
  2. class BuilderExample {
    
  3.  
    
  4.  public static void main(final String[] args) {
    
  5.  
    
  6.   final Waiter waiter = new Waiter();
    
  7.  
    
  8.   final PizzaBuilder hawaiianPizzaBuilder = new HawaiianPizzaBuilder();
    
  9.   final PizzaBuilder spicyPizzaBuilder = new SpicyPizzaBuilder();
    
  10.  
    
  11.   waiter.setPizzaBuilder(hawaiianPizzaBuilder);
    
  12.   waiter.constructPizza();
    
  13.  
    
  14.   final Pizza pizza = waiter.getPizza();
    
  15.  
    
  16.   waiter.setPizzaBuilder(spicyPizzaBuilder);
    
  17.   waiter.constructPizza();
    
  18.  
    
  19.   final Pizza anotherPizza = waiter.getPizza();
    
  20.  }
    
  21. }
    
Implementation in C#
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();
    }
  }
 
}
Implementation in C++
#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();
}
Implementation in PHP
<?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
Implementation in Ruby
# 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
Implementation in Python
#coding: utf-8
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 #meow!


Clipboard

To do:
Add more examples of use.


Abstract Factory Computer Science Design Patterns
Builder
Factory method


You have questions about this page?
Ask it here:


Create a new page on this book: