Builder „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.“
Obecné Vlastnosti Cíle vytváří objekty po částech řeší pořadí účel je řídit process, ne získat produkt Cíle vytvořit různé reprezentace podobným postupem zjednodušit rozšiřitelnost do budoucnosti přehledný zdrojový kód
Struktura Director Builder ConcreteBuilder Product řídí proces používá Builder a po krocích vytvoří objekt Builder definuje rozhrání ConcreteBuilder implementuje rozhrání Builderu definuje proces tvorby dané části Product reprezentuje vytvořený objekt
Sequence diagram Klient vytvoří Director a konfiguruje ho ConcreteBuilderem Director požádá ConcreteBuildera o vytvoření části výsledného objektu Klient získá vytvořený objekt od ConcreteBuilderu
Příklad: Fast food restaurace Části hamburger nápoje dezert hračka Jiné části - stejná konstrukce Literatura: http://www.cnblogs.com/xiuyusoft/archive/2011/06/17/2083540.html
Implementace Builderu interface typicky abstraktní třída virtuální metody definují, co má každý ConcreteBuilder umět ne nutně „void“ např. vracení vytvořených častí Productu Product žádná abstraktní třída Nemá smysl – vytvořené objekty se hodně odlišují V ConcreteBuilder-i: nově zkonstruované části zpravidla stačí obyčejně připojit, příklad s čtením RTF – další zkonvertované tokeny se přidávají na konec.
Souvislosti a důsledky Struktura konečného produktu vázaná na ConcreteBuilder interface pro konstrukci je stejný konkrétní implementační detaily skryté změna produktu = nový builder Oddělení konstrukce od reprezentace modularita, zapouzdření Umožňuje kontrolu konstrukce během jejího průběhu konstrukce produktu „step-by-step“ řeší kauzalitu při konstrukci jednotlivých bloků
Shrnutí Kdy použit Builder? Proč ho použit? objekt se skládá z více částí složitý proces tvorby částí očekává se rozšíření budování immutable s mnoho volitelnými parametry Proč ho použit? izolace konstrukce a reprezentace umožňuje změnit strukturu bez přepisování všech ConcreteBuilderů třídy mohou zůstat neměnné (immutable)
Kde použít Konstruktory s volitelnými vlastnostmi Řešení public class User { private final String firstName; //required private final String lastName; //required private final int age; //optional private final String phone; //optional private final String address; //optional } Konstruktory s volitelnými vlastnostmi chceme konstruktory s povinnými + všechny možné podmnožiny volitelných argumentů Nechceme veřejné „settery“ Ztratíme výhody neměnných (immutable) objektů Řešení Naprogramovat všechny možné konstruktory Použit volitelné argumenty Builder Pro .NET od verze 3.0 a C++ platí, že je možné použít koncept volitelných argumentů. Nicméně větší množství takových argumentů značně snižuje čitelnost kódu.
Kostruktory public User(String firstName, String lastName) { this(firstName, lastName, 0); } public User(String firstName, String lastName, int age) { this(firstName, lastName, age, ""); public User(String firstName, String lastName, int age, String phone) { this(firstName, lastName, age, phone, ""); public User(String firstName, String lastName, int age, String phone, String address) { this.firstName = firstName; this.lastName = lastName; this.age = age; this.phone = phone; this.address = address; // ... ještě další 4 konstruktory // rozšíření třídy User o 1 atribut problematické
Builder jediný argument konstruktoru třídy „User“: ConcreteBuilder třída „User“ získá Product od ConcreteBuilder public class User { private readonly String firstName; // required private readonly String lastName; // required private readonly int age; // optional private readonly String phone; // optional private readonly String address; // optional // 1 private konstruktor s jediným argumentem: ConcreteBuilder private User(UserBuilder builder) { this.firstName = builder.firstName; this.lastName = builder.lastName; this.age = builder.age; this.phone = builder.phone; this.address = builder.address; // náchylnosti na chyby se zbavíme použitím automapperu }
Builder public class UserBuilder { private readonly String firstName; private readonly String lastName; private int age; private String phone; private String address; public UserBuilder(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public UserBuilder age(int age) { this.age = age; return this; public UserBuilder phone(String phone) { this.phone = phone; return this; public UserBuilder address(String address) { this.address = address; return this; public User build() { return new User(this);
Fluent builder var b builder = new User.UserBuilder("John", "Doe"): builder = builder.age(30); builder = builder.phone("1234567"); builder = builder.address("Fake address 1234"); User user = builder.build();
Builder public class UserBuilder { private readonly String firstName; private readonly String lastName; private int age; private String phone; private String address; //novy atribut private String email; public UserBuilder(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public UserBuilder age(int age) { this.age = age; return this; public UserBuilder phone(String phone) { this.phone = phone; return this; public UserBuilder address(String address) { this.address = address; return this; //pridani atributu public UserBuilder email(String email) { this.email = email; return this; public User build() { return new User(this); Builder
Fluent builder var b builder = new User.UserBuilder("John", "Doe"): builder = builder.age(30); builder = builder.phone("1234567"); builder = builder.address("Fake address 1234"); Builder = builder.email (“mail@mail.com"); User user = builder.build();
Související NV Abstract Factory lze chápat jako limitní případ Builderu Builder vytvoří objekty postupně, po částích, vrátí až na požádání Produkty Builderu nemusí implementovat společný interface nasazení Abstract Factory preferujeme v případě, že celý objekt lze vytvořit „jediným voláním“, odpověď je získána hned Composite je často výsledkem práce Builderu
Příklad z reality @(Html.Kendo().Grid<Kendo.Mvc.Examples.Models.CustomerViewModel>() .Name("grid") .Columns(columns => { columns.Bound(c => c.ContactName).ClientTemplate( @"<div class='customer-photo' style='background-image: url(../content/web/Customers/#:data.CustomerID#.jpg);'></div> <div class='customer-name'>#: ContactName #</div>") .Width(240); columns.Bound(c => c.ContactTitle); columns.Bound(c => c.CompanyName); columns.Bound(c => c.Country).Width(150); }) .HtmlAttributes(new { style = "height: 550px;" }) .Scrollable() .Groupable() .Sortable() .Pageable(pageable => pageable .Refresh(true) .PageSizes(true) .ButtonCount(5)) .DataSource(dataSource => dataSource .Ajax() .Read(read => read.Action("Customers_Read", "Grid")) .PageSize(20) ) http://demos.telerik.com/aspnet-mvc/grid/index
Příklad z reality http://demos.telerik.com/aspnet-mvc/grid/index
Nepřihlášený uživatel Příklad z reality - SIS GUI stejný obsah položek jiné barvy, tvar Obsah podle přihlášeného uživatele jádro stejné další položky zobrazené/vynechané podle privilegií Nepřihlášený uživatel Přihlášený uživatel