Строитель (шаблон проектирования)
Строитель, (англ. Builder) — порождающий шаблон проектирования.
Цель
Отделяет конструирование сложного объекта от его представления, так что в результате одного и того же процесса конструирования могут получаться разные представления.
Плюсы
- позволяет изменять внутреннее представление продукта;
- изолирует код, реализующий конструирование и представление;
- дает более тонкий контроль над процессом конструирования.
Применение
- алгоритм создания сложного объекта не должен зависеть от того, из каких частей состоит объект и как они стыкуются между собой;
- процесс конструирования должен обеспечивать различные представления конструируемого объекта.
Примеры
Пример на Java
/** "Product" Maksatzadr*/
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 hawaiian_pizzabuilder = new HawaiianPizzaBuilder();
PizzaBuilder spicy_pizzabuilder = new SpicyPizzaBuilder();
waiter.setPizzaBuilder( hawaiian_pizzabuilder );
waiter.constructPizza();
Pizza pizza = waiter.getPizza();
}
}
Пример на С#
using System;
using System.Collections.Generic;
namespace Builder
{
public class MainApp
{
public static void Main()
{
// Create director and builders
Director director = new Director();
Builder b1 = new ConcreteBuilder1();
Builder b2 = new ConcreteBuilder2();
// Construct two products
director.Construct(b1);
Product p1 = b1.GetResult();
p1.Show();
director.Construct(b2);
Product p2 = b2.GetResult();
p2.Show();
// Wait for user
Console.Read();
}
}
// "Director"
class Director
{
// Builder uses a complex series of steps
public void Construct(Builder builder)
{
builder.BuildPartA();
builder.BuildPartB();
}
}
// "Builder"
abstract class Builder
{
public abstract void BuildPartA();
public abstract void BuildPartB();
public abstract Product GetResult();
}
// "ConcreteBuilder1"
class ConcreteBuilder1 : Builder
{
private readonly Product product = new Product();
public override void BuildPartA()
{
product.Add("PartA");
}
public override void BuildPartB()
{
product.Add("PartB");
}
public override Product GetResult()
{
return product;
}
}
// "ConcreteBuilder2"
class ConcreteBuilder2 : Builder
{
private readonly Product product = new Product();
public override void BuildPartA()
{
product.Add("PartX");
}
public override void BuildPartB()
{
product.Add("PartY");
}
public override Product GetResult()
{
return product;
}
}
// "Product"
class Product
{
private readonly List<string> parts = new List<string>();
public void Add(string part)
{
parts.Add(part);
}
public void Show()
{
Console.WriteLine("\nProduct Parts -------");
foreach (string part in parts)
Console.WriteLine(part);
}
}
}
Пример на C++
// Реализация на C++.
#include <iostream>
#include <memory>
#include <string>
// Product
class Pizza
{
private:
std::string dough;
std::string sauce;
std::string topping;
public:
Pizza() { }
~Pizza() { }
void SetDough(const std::string& d) { dough = d; };
void SetSauce(const std::string& s) { sauce = s; };
void SetTopping(const std::string& t) { topping = t; }
void ShowPizza()
{
std::cout << " Yummy !!!" << std::endl
<< "Pizza with Dough as " << dough
<< ", Sauce as " << sauce
<< " and Topping as " << topping
<< " !!! " << std::endl;
}
};
// Abstract Builder
class PizzaBuilder
{
protected:
std::auto_ptr<Pizza> pizza;
public:
PizzaBuilder() {}
virtual ~PizzaBuilder() {}
std::auto_ptr<Pizza> GetPizza() { return pizza; }
void createNewPizzaProduct() { pizza.reset (new Pizza); }
virtual void buildDough()=0;
virtual void buildSauce()=0;
virtual void buildTopping()=0;
};
// ConcreteBuilder
class HawaiianPizzaBuilder : public PizzaBuilder
{
public:
HawaiianPizzaBuilder() : PizzaBuilder() {}
~HawaiianPizzaBuilder(){}
void buildDough() { pizza->SetDough("cross"); }
void buildSauce() { pizza->SetSauce("mild"); }
void buildTopping() { pizza->SetTopping("ham and pineapple"); }
};
// ConcreteBuilder
class SpicyPizzaBuilder : public PizzaBuilder
{
public:
SpicyPizzaBuilder() : PizzaBuilder() {}
~SpicyPizzaBuilder() {}
void buildDough() { pizza->SetDough("pan baked"); }
void buildSauce() { pizza->SetSauce("hot"); }
void buildTopping() { pizza->SetTopping("pepperoni and salami"); }
};
// Director
class Waiter
{
private:
PizzaBuilder* pizzaBuilder;
public:
Waiter() : pizzaBuilder(NULL) {}
~Waiter() { }
void SetPizzaBuilder(PizzaBuilder* b) { pizzaBuilder = b; }
std::auto_ptr<Pizza> GetPizza() { return pizzaBuilder->GetPizza(); }
void ConstructPizza()
{
pizzaBuilder->createNewPizzaProduct();
pizzaBuilder->buildDough();
pizzaBuilder->buildSauce();
pizzaBuilder->buildTopping();
}
};
// Клиент заказывает две пиццы.
int main()
{
Waiter waiter;
HawaiianPizzaBuilder hawaiianPizzaBuilder;
waiter.SetPizzaBuilder (&hawaiianPizzaBuilder);
waiter.ConstructPizza();
std::auto_ptr<Pizza> pizza = waiter.GetPizza();
pizza->ShowPizza();
SpicyPizzaBuilder spicyPizzaBuilder;
waiter.SetPizzaBuilder(&spicyPizzaBuilder);
waiter.ConstructPizza();
pizza = waiter.GetPizza();
pizza->ShowPizza();
return EXIT_SUCCESS;
}
Пример на JavaScript
// Product
function Pizza() {
var dublicate = this; // постоянная ссылка на инстанцируемый объект для вызова при любом this
var dough;
var sauce;
var topping;
this.setDough = function(val) {
dough = val;
};
this.setSauce = function(val) {
sauce = val;
};
this.setTopping = function(val) {
topping = val;
};
// из-за особенностей языка, геттеры (пусть они нам и не понадобятся)
// должны быть определены в той же функции, что и локальные переменные
this.getDough = function() {
return dough;
};
this.getSauce = function() {
return sauce;
};
this.getTopping = function() {
return topping;
};
// мы должны создать метод, изменяющий св-ва уже созданного объекта
// (см. createNewPizzaProduct)
this.clear = function() {
dublicate.setDough(undefined);
dublicate.setSauce(undefined);
dublicate.setTopping(undefined);
};
}
// Abstract Builder
function PizzaBuilder() {
var pizza = new Pizza();
this.getPizza = function() {
return pizza;
};
this.createNewPizzaProduct = function() {
// если мы просто поменяем зн-е переменной pizza, то изменение никак
// не отразится на дочерних классах, т.к. внутри них переменная pizza
// ссылается на «старую» область памяти
pizza.clear();
// если внутри реализаций (HawaiianPizzaBuilder, SpicyPizzaBuilder)
// мы, вместо переменной pizza, будем использовать метод getPizza,
// то можно использовать
// pizza = new Pizza();
// и метод clear у Pizza не понадобится
};
this.buildDough = function(val) { };
this.buildSauce = function(val) { };
this.buildTopping = function(val) { };
}
// ConcreteBuilder
function HawaiianPizzaBuilder() {
PizzaBuilder.call(this);
var pizza = this.getPizza(); // имитация protected
this.buildDough = function() { pizza.setDough("cross"); };
this.buildSauce = function() { pizza.setSauce("mild"); };
this.buildTopping = function() { pizza.setTopping("ham+pineapple"); };
}
function SpicyPizzaBuilder() {
PizzaBuilder.call(this);
var pizza = this.getPizza();
this.buildDough = function() { pizza.setDough("pan baked"); };
this.buildSauce = function() { pizza.setSauce("hot"); };
this.buildTopping = function() { pizza.setTopping("pepperoni+salami"); };
}
// Director
function Waiter() {
var pizzaBuilder;
this.setPizzaBuilder = function(builder) {
pizzaBuilder = builder;
};
this.getPizza = function() {
return pizzaBuilder.getPizza();
};
this.constructPizza = function() {
pizzaBuilder.createNewPizzaProduct();
pizzaBuilder.buildDough();
pizzaBuilder.buildSauce();
pizzaBuilder.buildTopping();
};
}
// Клиент заказывает две пиццы
var pizza;
var waiter = new Waiter();
var hawaiianPizzaBuilder = new HawaiianPizzaBuilder();
waiter.setPizzaBuilder( hawaiianPizzaBuilder );
waiter.constructPizza();
pizza = waiter.getPizza();
alert( pizza.getDough() +", "+ pizza.getSauce() +", "+ pizza.getTopping() );
var spicyPizzaBuilder = new SpicyPizzaBuilder();
waiter.setPizzaBuilder( spicyPizzaBuilder );
waiter.constructPizza();
pizza = waiter.getPizza();
alert( pizza.getDough() +", "+ pizza.getSauce() +", "+ pizza.getTopping() );
Вариант (урезанный) без использования локальной переменной dublicate и метода clear у Pizza
// Product
function Pizza() {
var dough;
var sauce;
var topping;
// методы setDough, setSauce, setTopping, getDough, getSauce, getTopping без изменений
// метод clear не нужен
}
// Abstract Builder
function PizzaBuilder() {
var pizza = new Pizza();
this.getPizza = function() {
return pizza;
};
this.createNewPizzaProduct = function() {
// внутри реализаций (HawaiianPizzaBuilder, SpicyPizzaBuilder)
// будем использовать метод getPizza
pizza = new Pizza();
};
this.buildDough = function(val) { };
this.buildSauce = function(val) { };
this.buildTopping = function(val) { };
}
// ConcreteBuilder
function HawaiianPizzaBuilder() {
PizzaBuilder.call(this);
// вместо имитации protected будем использовать ссылку на геттер
var pizza = this.getPizza;
this.buildDough = function() { pizza().setDough("cross"); };
this.buildSauce = function() { pizza().setSauce("mild"); };
this.buildTopping = function() { pizza().setTopping("ham+pineapple"); };
}
function SpicyPizzaBuilder() {
PizzaBuilder.call(this);
var pizza = this.getPizza;
this.buildDough = function() { pizza().setDough("pan baked"); };
this.buildSauce = function() { pizza().setSauce("hot"); };
this.buildTopping = function() { pizza().setTopping("pepperoni+salami"); };
}
// Director
function Waiter() {
// без изменений
}
// Клиент заказывает две пиццы
var pizza;
var waiter = new Waiter();
var hawaiianPizzaBuilder = new HawaiianPizzaBuilder();
waiter.setPizzaBuilder( hawaiianPizzaBuilder );
waiter.constructPizza();
pizza = waiter.getPizza(); // возвращенный объект теперь не изменится при constructPizza
waiter.constructPizza();
pizza2 = waiter.getPizza();
alert( pizza.getDough() +", "+ pizza.getSauce() +", "+ pizza.getTopping() );
alert( pizza2.getDough() +", "+ pizza2.getSauce() +", "+ pizza2.getTopping() );
var spicyPizzaBuilder = new SpicyPizzaBuilder();
waiter.setPizzaBuilder( spicyPizzaBuilder );
waiter.constructPizza();
pizza = waiter.getPizza();
alert( pizza.getDough() +", "+ pizza.getSauce() +", "+ pizza.getTopping() );
порождающие шаблоны проектирования |
абстрактная фабрика | строитель | фабричный метод | прототип | одиночка | отложенная инициализация |
bg:Строител (шаблон) ca:Builder de:Erbauer (Entwurfsmuster) en:Builder pattern es:Builder (patrón de diseño) fr:Monteur (patron de conception) it:Builder ja:Builder パターン ko:빌더 패턴 pl:Budowniczy (wzorzec projektowy) pt:Builder uk:Будівник (шаблон проектування) zh:生成器 (设计模式)
Если вам нравится SbUP.com Сайт, вы можете поддержать его - BTC: bc1qppjcl3c2cyjazy6lepmrv3fh6ke9mxs7zpfky0 , TRC20 и ещё....