A Builder tervezési minta

A Factory és Abstract Factory mintákkal, mint már tudjuk, menetközben tudunk létrehozni a környezetnek éppen megfelelő objektumot. De mi van akkor, ha nem csak különböző objektumok közül kell választani, hanem adott objektum típuson belül is el kell térni a gyártáskor? Részletek a cikkben.

A Builder mintában két fő résztvevő van (ha úgy tetszik két fő szerep van) a director (rendező – itthon inkább főnök :)) és a builder (munkás). A director kontrollálja az építést (building), ő mondja meg melyik részegység milyen változatban, mikor és hogyan kerül bele az objektumba. A builder viszont tudja, hogyan kell az objektumot összeszerelni, ismeri az összerakási folyamatokat külön-külön, de a teljes munkálathoz szüksége van a directorra, aki megmondja a kívánt pontos összeszerelési sorrendet, specifikációt.

Mikor is kellene a Builder mintát használni?

Ha az összeállítandó objektumunk már megfelelően komplex, annak létrehozási folyamatát el kell függetleníteni magától objektumtól. Adott objektum több különböző változatát kell tudni legyártani menet közben.

A Builder minta jellemzői

A director és a builder előírás szerint absztrakt osztályúak a variálhatóság és a szabványosítás miatt. A Builder mindig egy igen összetett objektum létrehozására koncentrál, lépésről lépésre. Az összerakás után utolsó lépésként mindig megkapjuk a produktumot. Gyakran megfigyelhető, hogy egy Factory kód fejlődik tovább Abstract Factory-vá, Prototype, vagy Builder mintává (egyre komplexebb lesz).

Jegyezzük meg a Builder minta képes összedolgozni más mintákkal, mint például a Singelton (szükség esetén).

Példa a Builder mintára:

Mivel egyre összetettebb mintákat nézünk, egyre bonyolultabb példákat kellene néznünk, de az átláthatóság miatt nem dolgozom ki a példák minden részletét, előfordul, hogy már nem teljesen életszerű egy-egy példa.

Vegyük a következő esetet, egy html honlapot kell előállítanunk, és mint tudjuk ezek felépítésükben egyformák, hasonlóak, de mindig eltérőek. Röviden, lesz egy builder-ünk, aki tudja hogyan kell a lap egyes részeit megépíteni, konfigurálni, és ha az kész akkor átadja a director-nak. A director-unk pedig tudja, hogy a lapnak hogyan kell felépülnie, mit kell tartalmaznia, valamint ha kész a lap, azt tovább adja. Egy példakód többet ér ezer szónál, szóval lássuk.

A Builder minta PHP kódja:

<?php
// Az absztrakt builder osztály
abstract class AbstractBuilder{
abstract function setTitle($title);
abstract function setCharset($charset);
abstract function addContent($content);
abstract function constructPage();
abstract function getPage();
}
// Az absztrakt director osztály
abstract class AbstractDirector{
abstract function __construct(AbstractBuilder $builder);
abstract function buildPage();
abstract function getPage();
}
// A html4 oldal objektuma, tulajdonképpen ez lesz majd a produktum
class PageHTML4{
private $output = null; // a teljes lap html kódja
private $title = null; // a lap címe
private $charset = null; // a karakterkódolás típusa
private $content = null; // a tartalmi rész
function __construct() {
// inicializációs rész
}
function showPage() { // visszaadja a kész lap forrását
return $this->output;
}
function setTitle($title) { // beállítja a lap címét
$this->title = $title;
}
function setCharset($charset) { // beállítja a lap karakterkódolását
$this->charset = $charset;
}
function addContent($content) { // hozzáad a tartalmi részhez
$this->content .= $content;
}
function constructPage() { // elkészíti a html forrást
$this->output = '<html>';
$this->output .= '<head><title>'.$this->title.'</title><meta http-equiv="content-type" content="text/html; charset='.$this->charset.'"></head>';
$this->output .= '<body>';
$this->output .= $this->content;
$this->output .= '</body>';
$this->output .= '</html>';
}
}
// A html builder
class HTMLBuilder extends AbstractBuilder {
private $page = null; // a készítendő lap
function __construct() { // előkészít egy üres lap objektumot
$this->page = new PageHTML4();
}
function setTitle($title) { // beállítja a lap címét
$this->page->setTitle($title);
}
function setCharset($charset) { // beállítja a lap karakterkódolását
$this->page->setCharset($charset);
}
function addContent($content) { // hozzáad a tartalmi részhez
$this->page->addContent($content);
}
function constructPage() { // elkészíti a html forrást
$this->page->constructPage();
}
function getPage() { // visszaadja az elkészült lap objektumot
return $this->page;
}
}
// A html director
class HTMLDirector extends AbstractDirector {
private $builder = null; // a builder változója
public function __construct(AbstractBuilder $builder) { // előkészít egy buildert
$this->builder = $builder;
}
public function buildPage() { // összepakolja a lapot
$this->builder->setTitle('Builder minta PHP-ben');
$this->builder->setCharset('UTF-8');
$this->builder->addContent('1. Tartalom <br/>');
$this->builder->addContent('2. Tartalom <br/>');
$this->builder->addContent('3. Tartalom <br/>');
$this->builder->constructPage();
}
public function getPage() { // a kész lap objektumot adja vissza
return $this->builder->getPage();
}
}
$pageBuilder = new HTMLBuilder(); // builder objektum
$pageDirector = new HTMLDirector($pageBuilder); // a director, aki most megkapja, melyik builder-rel kell dolgoznia
$pageDirector->buildPage(); // a lap elkészítésének parancsa
$page = $pageDirector->getPage(); // visszakérjük a lapot
echo $page->showPage(); // lekérjük a lap objektumtól annak kimenetét.
//1. Tartalom
//2. Tartalom
//3. Tartalom
?>

Amint látjuk az absztrakt osztályok lehetővé teszik a nagyfokú "variálhatóságot", és persze megteremtik az egységes builderek és directorok osztály alapjait. Létrehoztunk egy PageHTML4 osztályt is, ami maga az elkészítendő objektum. A HTMLBuilder és a HTMLDirector pedig maguk alkotják a kreációs részt, a HTMLBuilder a PageHTML4 objektumot fogja beállítgatni, minden egyes lépést külön-külön, önálló függvényekkel támogat, de összefüggő lépésekre nem képes. A műveletsorokat láthatóan a HTMLDirector végzi, pontosabban végezteti a buildPage() függvényben. A teszt során észrevehető, hogy a director megkapja a builder-t, amivel a munkát el fogja végeztetni, majd a végeredményt a getPage() metódus adja vissza a hívónak, ügyfélnek.

Az absztrakt osztályoknak és a Builder mintának köszönhető, hogy több kényelmes megoldást is magában hordoz a kód arra az esetre, ha menetközben kellene eldönteni, hogy HTML4 vagy HTML5 oldalt kell készíteni.

A Builder minta Javascript kódja:


// A html4 oldal objektuma, tulajdonképpen ez lesz majd a produktum
function PageHTML4() {
this.output = ''; // a teljes lap html kódja
this.title = ''; // a lap címe
this.charset = ''; // a karakterkódolás típusa
this.content = ''; // a tartalmi rész
this.showPage = function(){ // visszaadja a kész lap forrását
return this.output;
}
this.setTitle = function(title){ // beállítja a lap címét
this.title = title;
}
this.setCharset = function(charset){ // beállítja a lap karakterkódolását
this.charset = charset;
}
this.addContent = function(content){ // hozzáad a tartalmi részhez
this.content += content;
}
this.constructPage = function(){ // elkészíti a html forrást
this.output = '<html>';
this.output += '<head><title>'+this.title+'</title><meta http-equiv="content-type" content="text/html; charset='+this.charset+'"></head>';
this.output += '<body>';
this.output += this.content;
this.output += '</body>';
this.output += '</html>';
}
}
// A html director
function HTMLDirector(builder){
this.builder = builder;

this.buildPage = function(){
this.builder.setTitle('Builder minta Javascript-ben');
this.builder.setCharset('UTF-8');
this.builder.addContent('1. Tartalom <br/>');
this.builder.addContent('2. Tartalom <br/>');
this.builder.addContent('3. Tartalom <br/>');
this.builder.constructPage();
}

this.getPage = function(){ // a kész lap objektumot adja vissza
return this.builder.getPage();
}
}
// A html builder
function HTMLBuilder() {
this.page = new PageHTML4(); // a készítendő lap
this.setTitle = function(title) { // beállítja a lap címét
this.page.setTitle(title);
}
this.setCharset = function(charset) { // beállítja a lap karakterkódolását
this.page.setCharset(charset);
}
this.addContent = function(content) { // hozzáad a tartalmi részhez
this.page.addContent(content);
}
this.constructPage = function() { // elkészíti a html forrást
this.page.constructPage();
}
this.getPage = function() { // visszaadja az elkészült lap objektumot
return this.page;
}
}
var pageBuilder = new HTMLBuilder(); // builder objektum
var pageDirector = new HTMLDirector(pageBuilder); // a director objektum
pageDirector.buildPage(); // a lap elkészítésének parancsa
var page = pageDirector.getPage(); // visszakérjük a lapot
console.log(page.showPage()); // lekérjük a lap objektumtól annak kimenetét.

A kódban jól látható, hogy szinte egy az egyben átemelhető a PHP-s kód, legalábbis részeiben. Az absztrakt osztályoktól eltekintve a Javascriptes kód is ugyanúgy működik, ezért külön nem magyarázom el. Az absztrakt elemeket most kihagytam a hely és az átláthatóság miatt, de amint azt előző cikkekben lehetett látni, nem ördöngösség szimulálni az absztrakt osztályokat Javascriptben.

Ha tetszett a cikk, véleményed van, kiegészítenéd vagy hibát találtál, írj a cikk alatt egy kommentet, vagy írd meg emailben.

Buy and Trade Bitcoin at Binance