Návrhový vzor stavitel () je softwarový návrhový vzor. Patří do skupiny vytvářejících (creational) návrhových vzorů.
Slouží k abstrahování tvorby složitých objektů. A to tak, aby stejné výrobní schéma mohlo být použito pro tvorbu různých objektů. Způsob konstrukce těchto objektů je tedy stejný, jednotlivé kroky se však liší. Často se používá společně s návrhovým vzorem Kompozit (Composite), konkrétně vytvářené objekty mohou být typu kompozit, dle zmíněného vzoru.
Obecný popis
Definice „Návrhový vzor oddělující konstrukci složitých objektů od jejich reprezentace. Čímž je možné použít stejný proces konstrukce pro rozdílné reprezentace.“Gang of Four Content Creation Wiki for People Projects And Patterns in Software Development.
Stručná historie První publikace popisující myšlenku návrhových vzorů pochází z roku 1977.Alexander, Christopher; Sara Ishikawa, Murray Silverstein, Max Jacobson, Ingrid Fiksdahl-King, Shlomo Angel (1977). A Pattern Language: Towns, Buildings, Construction. New York: Oxford University Press. . Název „builder pattern“ se však v odborné literatuře před rokem 1995 a zveřejnění knihy návrhové vzory nevyskytuje. Vzorem se dále zabývá Josua Bloch ve své knize Effective Java 5.Joshua, Bloch (2008). Effective Java. Addison-Wesley Java series. . Kdy navrhuje alternativu zaměřenou na odstranění opakování kódu v třídách Concrete Builder (viz Struktura).
Pohled programátora
Motivace užití Použití Stavitele je vhodné u rozdílných tříd s podobným procesem konstrukce. Samotné třídy mohou být rozdílné a nezávislé. Tím se liší od Abstraktní továrny, která slouží k tvorbě podobných objektů. Podobných z hlediska prezentace v systému Windows, nebo Mac. Důvodem pro použití je oddělení vytváření složitých objektů od jejich prezentace, které by mělo vést k přehlednějšímu kódu.
Struktura střed|500px|Diagram
Director Třída řídící proces vytváření objektů.
„Instance třídy Director získává od klienta určení, který Builder má použít. Tím je i definováno, který produkt bude vyráběn. Director řídí vytvoření produktu voláním metod pro zhotovení jednotlivých částí z rozhraní Builder. Po vytvoření požadované části výsledného produktu je tato do něho začleněna. Výsledný produkt je možné získat použitím metody getResult. Director je odstíněn od způsobu, jakým se vytvářejí konkrétní části a jak se skládají. Určuje, ovšem kdy se mají části vyrobit a tím řídí proces vytváření produktů.“Miloš Dvořák. Návrhové vzory
Builder Abstraktní rozhraní pro tvorbu objektů (produkt). Metoda buildPart slouží k vytvoření konkrétní části objektu, o její volání se stará Director.
Concrete Builder Implementace rozhraní Builder schopná vytvářet další objekty. Vytváří a sestavuje součásti pro tvorbu složitých objektů. Metoda getResult slouží k předání výsledného produktu a volá ji zpravidla klient, který konstrukci objektu vyvolal.
„Která z možných tříd ConcreteBuilder je použita pro vytvoření objektu, může být rozhodnuto na základě použitých dat, která mají být zobrazena. Například pokud má být zobrazeno několik položek, je klientu předán jako Product seznam tvořen „checkBoxy“. V případě více položek bude vytvořen a předán rozbalovací seznam. Samozřejmě kromě zobrazovaných dat mohou hrát roli i jiné faktory. Directoru je předán jako parametr instance ConcreteBuilder a Director, s využitím metod nadefinovaných v rozhraní, řídí proces vytváření produktu.“
Příklad užití Konstrukci provádí konkrétní implementace rozhraní Builder, ale proces konstrukce řídí třída Director. Například klient přijde k automatu a chce kapučíno. O tvorbu kapučína se stará automat (direktor), klient si zvolí nápoj stiskem tlačítka. Automat provádí vždy ty samé kroky, existuje tedy jediné výrobní schéma, ale podle stisku tlačítka mohou být některé kroky vynechány (cukr), některé vzájemně zaměnitelné (kapučíno vs. čaj do kelímku). O realizaci těchto jednotlivých kroků se starají různé implementace třídy Builder (Stavitel čajů, stavitel kafí, apod.). Nápoj je možné odebrat teprve poté, kdy jsou všechny kroky konstrukce dokončeny.
Tedy zjednodušeně: Ke konstrukci jednoho objektu slouží právě jeden Builder, proces konstrukce řídí Direktor, objekt ke konstrukci vybírá klient předáním Builderu Direktoru, po ukončení konstrukce si z Builderu pouze vybere výsledek metodou getResult.
Stavitelů ke konstrukci jednoho objektu může být voláno i více. Například pokud je výsledný produkt Composite může být při jeho konstrukci voláno více stavitelů, případně stejný stavitel několikrát za sebou. Tak je zajištěno například vytváření složitých objektů grafického rozhraní operačního systému.
Výsledek užití „Je vytvořen princip konstrukce objektů, který je variabilní na základě používaných dat nebo nadefinování věcné logiky. Klient je odstíněn od tvorby objektů a pouze prezentuje výsledek.“
Užitečné tipy při užití Builder slouží ke konstrukci složitých objektů po jednotlivých krocích. Zatímco Abstraktní továrna se používá při tvorbě blízké skupiny objektů (jednoduchých i komplexních). Abstraktní továrna vrací produkt v jednom kroku, u vzoru Builder je výsledný objekt vrácen po ukončení a jako výsledek konstrukce. Návrhový vzor Stavitel se obvykle používá k tvorbě objektů typu Kompozit. Tam kde je třeba dodatečná flexibilita, dochází v návrhu k vývoji od návrhového vzoru Továrna, přes, či k Abstraktní továrně, Prototypu a Staviteli. Tedy od jednodušších, snáze přizpůsobitelných vzorů s možností tvorby podtříd k pružnějším, ale složitějším vzorům. Vytvářející návrhové vzory se mohou vzájemně doplňovat. Například Stavitel může k určení, který objekt se má vytvořit Abstraktní továrnu, nebo jiný návrhový vzor. Dále Abstraktní továrna, Stavitel i Prototyp mohou použít Jedináčka (Singleton) ve svých implementacích. Vzor Stavitel je dobrým kandidátem pro implementaci plynulého rozhraní (řetězcovité volání – fluent interface).
Příklady Implementace
Java /** "Product" */ class Pizza { private String dough = ""; private String sauce = ""; private String topping = "";
public void setDough(String dough) { this.dough = dough; } public void setSauce(String sauce) { this.sauce = sauce; } public void setTopping(String topping) { this.topping = topping; }
/** "Abstract Builder" */ 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();
/** "ConcreteBuilder" */ class HawaiianPizzaBuilder extends PizzaBuilder { public void buildDough() { pizza.setDough("cross"); } public void buildSauce() { pizza.setSauce("mild"); } public void buildTopping() { pizza.setTopping("ham+pineapple"); }
/** "ConcreteBuilder" */ class SpicyPizzaBuilder extends PizzaBuilder { public void buildDough() { pizza.setDough("pan baked"); } public void buildSauce() { pizza.setSauce("hot"); } public void buildTopping() { pizza.setTopping("pepperoni+salami"); }
/** "Director" */ class Waiter { 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();
/** A customer ordering a pizza. */ class BuilderExample { public static void main(String[] args) { Waiter waiter = new Waiter(); PizzaBuilder hawaiianPizzaBuilder = new HawaiianPizzaBuilder(); PizzaBuilder spicyPizzaBuilder = new SpicyPizzaBuilder();
waiter.setPizzaBuilder(hawaiianPizzaBuilder); waiter.constructPizza();
Pizza pizza = waiter.getPizza();
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();
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: 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_(nullptr)
~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();