Építő programtervezési minta

A számítógép-programozásban az építő programtervezési minta egy létrehozási minta. Az Absztrakt gyár és a Gyártó metódus programtervezési mintákkal szemben, melyek célja a többalakúság alkalmazása, az építő minta célja, hogy alternatívát találjon a teleszkópos konstruktor anti-mintára. Ez az anti-minta akkor jelenik meg, amikor az objektum konstruktor paraméter-kombinációk számának növekedése a konstruktorok exponenciális listáját eredményezi. Ahelyett hogy nagyszámú konstruktort alkalmazna, az Építő minta egy másik objektumot, az építőt alkalmazza, mely minden egyes inicializációs paramétert lépésről lépésre kap meg, majd egyben adja vissza az elkészült objektumot.

Az építő mintának másik előnye is van. Használhatjuk olyan objektumoknál, melyek lapos adatot tartalmaznak (HTML kód, SQL lekérdezés, X.509 tanúsítvány ...), más szóval olyan adatot, melyet nem lehet egyszerűen szerkeszteni. Az ilyen típusú adatokat nem lehet lépésről lépésre szerkeszteni, csakis egyszerre. A legjobb módja, hogy ilyen objektumot hozzunk létre, hogy építő osztályt alkalmazunk.

Az Építő minta gyakran Összetétel tervezési mintát készít. Előfordul, hogy a minták Gyár mintát alkalmazva indulnak (kevéssé bonyolult, jobban testre szabható, az alosztályok száma megnövekedhet) mely továbbfejlődik Absztrakt Gyárrá, Prototípussá vagy Építővé (rugalmasabb, összetettebb), ahogy a fejlesztő felfedezi, hogy hol van szükség nagyobb rugalmasságra. Néha a létrehozási minták kiegészítik egymást: az Építő minta használhat egy másik mintát, hogy megállapítsa, milyen más komponensek jönnek létre. Az Építők jól alkalmazhatók könnyed interfészekhez.

Az itt bemutatott Építő minta egy leegyszerűsített és speciális esete az elvontabb általános fogalomnak. Ideális esetben az Építő tervezési minta két fő részből áll: 1) egy Igazgató és 2) egy Építő komponensből. Az igazgató rész tartalmazza, hogy az építőt hogyan hívjuk meg egy objektum megépítésére. Jellemzően az Építő elvonatkoztat az objektum reprezentációtól, ezáltal támogatva a különböző objektummegvalósításokat. A kliens megkéri az Igazgatót, hogy építsen egy objektumot egy speciális Építő segítségével, majd megkéri az Építőt, hogy végezze el a feladatot.

Definíció

[szerkesztés]

Az Építő tervezési minta célja az összetett objektumok kialakításának és reprezentációjának szétválasztása. Ezáltal ugyanaz az építési folyamat különböző megvalósításokat eredményezhet.

Felépítése

[szerkesztés]
Builder Structure
Builder Structure

Builder: Absztrakt interfész objektumok létrehozására (termék). Concrete Builder: A Builder végrehajtását biztosítja. Olyan objektum, mely képes más objektumokat létrehozni. Létrehozza és összerakja az objektum részeit, melyek az építéshez szükségesek.

Értékelése

[szerkesztés]
  • Lehetővé teszi a termék belső reprezentációjának megváltoztatását.
  • Magába zárja a konstrukciót és a reprezentációt.
  • Lehetővé teszi az építési folyamat lépésenkénti ellenőrzését.
  • Minden termékhez más Concrete Builder kell.
  • Az építő osztályoknak megváltoztathatóknak kell lenniük.[1]

Pszeudókód

[szerkesztés]

Van egy Car (Autó) osztályunk. A probléma az, hogy egy autónak sok beállítása lehet. Ha kombinálnánk minden lehetőséget, az ehhez az osztályhoz nagyon nagyszámú konstruktort eredményezne. Létrehozunk egy osztályt,CarBuilder néven. A CarBuilder-hez (AutóÉpítő) ezért minden beállítást lépésről lépésre fogunk elküldeni, és csak ezután készítjük el a végleges autót a helyes beállításokkal:

class Car is
 Lehet benne GPS, utazástervező és változó számú ülés. Lehet városi autó, sportautó vagy kabrió.

class CarBuilder is
 method getResult() is
 output: egy Autó a helyes beállításokkal
 Megépít és visszaad egy autót.
 method setSeats(number) is
 input: az autóban található ülések száma,
 megmondja az építőnek az ülések számát
 method setCityCar() is
 Emlékezteti az építőt, hogy az autó városi autó.

 method setCabriolet() is
 Emlékezteti az építőt, hogy az autó kabrió.

 method setSportsCar() is
 Emlékezteti az építőt, hogy az autó sportkocsi.

 method setTripComputer() is
 Emlékezteti az építőt, hogy az autóban van utazástervező.

 method unsetTripComputer() is
 Emlékezteti az építőt, hogy az autóban nincs utazástervező.

 method setGPS() is
 Emlékezteti az építőt, hogy az autóban van globális helymeghatározó rendszer.

 method unsetGPS() is
 Emlékezteti az építőt, hogy az autóban nincs globális helymeghatározó rendszer.

Construct a CarBuilder called carBuilder
carBuilder.setSeats(2)
carBuilder.setSportsCar()
carBuilder.setTripComputer()
carBuilder.unsetGPS()
car := carBuilder.getResult()

C# példa

[szerkesztés]

Ez a minta az objektumokat az interfész alapján hozza létre, ugyanakkor engedi az alosztálynak, hogy válassza, melyik osztályból szeretne példányosítani. Továbbá végső kontrollja van az építési folyamaton. Létezik egy koncepció az Építő minta megvalósításában az Igazgató számára. Az Igazgató igazából létrehozza az objektumot és ezután néhány feladatot is lefuttat.

//IVSR: Builder Pattern
    public interface IBuilder
    {
        string RunBuilderTask1();
        string RunBuilderTask2();
    }
 
    public class Builder1 : IBuilder
    {
        public string RunBuilderTask1()
        {
            throw new ApplicationException("Task1");
        }
 
        public string RunBuilderTask2()
        {
            throw new ApplicationException("Task2");
        }
    }
 
    public class Builder2 : IBuilder
    {
        public string RunBuilderTask1()
        {
            return "Task3";
        }
 
        public string RunBuilderTask2()
        {
            return "Task4";
        }
    }
 
    public class Director
    {
        public IBuilder CreateBuilder(int type)
        {
            IBuilder builder = null;
            if (type == 1)
                builder = new Builder1();
            else
                builder = new Builder2();
            builder.RunBuilderTask1();
            builder.RunBuilderTask2();
            return builder;
        }
    }

Az Építő minta esetén látható, hogy az Igazgató maga használja a CreateBuildert, ezzel létrehozva az építő példányt. Így amikor a tényleges Építő létrejön, adhatunk neki néhány feladatot.

C++ példa

[szerkesztés]
////// Terméknyilatkozatok és inline implementáció //////
class Product{
	public:
		// ezt az osztályt használja a termék megépítésére
		class Builder;

	private:
		// az érvényes objektum létrehozásához szükséges változók
		int i;
		float f;
		char c;

		// csak egy egyszerű konstruktor - a továbbiakat az Építő kezeli
		Product( const int i, const float f, const char c ) : i(i), f(f), c(c){}

	public:
		// Termékspecifikus funkciók
		void print();
		void doSomething();
		void doSomethingElse();
};

class Product::Builder{
	private:
		// A Product osztály objektumainak létrehozásához szükséges változók
		int i;
		float f;
		char c;

	public:
		// változók alapértelmezett értékei
		static const int defaultI = 1;
		static const float defaultF = 3.1415f;
		static const char defaultC = 'a';

		// Építő létrehozása a hozzárendelt alapértékekkel
		// (A C++11-ben ehelyett egyszerűen hozzárendelhetjük őket a deklarációkor)
		Builder() : i( defaultI ), f( defaultF ), c( defaultC ){}

		// Egyedi értékeket ad a termék létrehozásnak
		// Visszatér az Építő gyors és inline alkalmazásra
		Builder& setI( const int i ){ this->i = i; return *this; }
		Builder& setF( const float f ){ this->f = f; return *this; }
		Builder& setC( const char c ){ this->c = c; return *this; }

		// elkésziti a gyaktan keresett specifikus termékeket
		// Visszatér az Építő gyors és inline elkalmazásra
		Builder& setProductP(){
			this->i = 42;
			this->f = -1.0f/12.0f;
			this->c = '@';

			return *this;
		}

		// elkésziti a kivánt terméket
		Product build(){
			// itt opcionálisan ellenőrizhetjük a változókonzisztenciát
			// illetve hogy a termék megépíthető-e a kapott információk alapján

			return Product( this->i, this->f, this->c );
		}
};
///// termék végrehajtása /////
#include <iostream>

void Product::print(){
	using namespace std;

	cout << "Product internals dump:" << endl;
	cout << "i: " << this->i << endl;
	cout << "f: " << this->f << endl;
	cout << "c: " << this->c << endl;
}

void Product::doSomething(){}
void Product::doSomethingElse(){}

//////////////////// Builder használata (az Igazgató helyettesítése)
int main(){
	// egyszerűsitett használat
	Product p1 = Product::Builder().setI(2).setF(0.5f).setC('x').build();
	p1.print(); // test p1

	// haladóknak
	Product::Builder b;
	b.setProductP();
	Product p2 = b.build(); //  Product P objektum 
	b.setC('!'); //  Product P objektum testreszabása
	Product p3 = b.build();
	p2.print(); // test p2
	p3.print(); // test p3
}
/**
 * Represents the product created by the builder.
 */
class Car {
    private int wheels;
    private String color;

    public Car() {
    }

    @Override
    public String toString() {
        return "Car [wheels = " + wheels + ", color = " + color + "]";
    }

    public int getWheels() {
        return wheels;
    }

    public void setWheels(final int wheels) {
        this.wheels = wheels;
    }

    public String getColor() {
        return color;
    }

    public void setColor(final String color) {
        this.color = color;
    }
}

/**
 * The builder abstraction.
 */
interface CarBuilder {
    CarBuilder setWheels(final int wheels);

    CarBuilder setColor(final String color);

    Car build();
}

class CarBuilderImpl implements CarBuilder {
    private Car car;

    public CarBuilderImpl() {
        car = new Car();
    }

    @Override
    public CarBuilder setWheels(final int wheels) {
        car.setWheels(wheels);
        return this;
    }

    @Override
    public CarBuilder setColor(final String color) {
        car.setColor(color);
        return this;
    }

    @Override
    public Car build() {
        return car;
    }
}

public class CarBuildDirector {
    private CarBuilder builder;

    public CarBuildDirector(final CarBuilder builder) {
        this.builder = builder;
    }

    public Car construct() {
        return builder.setWheels(4)
                      .setColor("Red")
                      .build();
    }

    public static void main(final String[] arguments) {
        CarBuilder builder = new CarBuilderImpl();
        CarBuildDirector carBuildDirector = new CarBuildDirector(builder);
        System.out.println(carBuildDirector.construct());
    }
}
abstract class GetterSetter
{
    public function __get($name)
    {
        $method = sprintf('get%s', ucfirst($name));

        if (!method_exists($this, $method)) {
            throw new Exception();
        }

        return $this->$method();
    }

    public function __set($name, $v)
    {
        $method = sprintf('set%s', ucfirst($name));

        if (!method_exists($this, $method)) {
            throw new Exception();
        }

        $this->$method($v);
    }
}

//Represents a product created by the builder
class Car extends GetterSetter
{
    private $wheels;
    private $colour;

    function __construct()
    {

    }

    public function setWheels($wheels)
    {
        $this->wheels = $wheels;
    }

    public function getWheels()
    {
        return $this->wheels;
    }

    public function setColour($colour)
    {
        $this->colour = $colour;
    }

    public function getColour()
    {
        return $this->colour;
    }
}

//The builder abstraction
interface ICarBuilder
{
    public function SetColour($colour);
    public function SetWheels($count);
    public function GetResult();
}

//Concrete builder implementation
class CarBuilder implements ICarBuilder
{
    private $_car;

    function __construct()
    {
        $this->_car = new Car();
    }

    public function SetColour($colour)
    {
        $this->_car->setColour($colour);
    }

    public function SetWheels($count)
    {
        $this->_car->setWheels($count);
    }

    public function GetResult()
    {
        return $this->_car;
    }
}

//The director
class CarBuildDirector
{
    public $builder;

    function __construct($color = "White", $wheels = 4)
    {
        $this->builder = new CarBuilder();

        $this->builder->SetColour($color);
        $this->builder->SetWheels($wheels);
    }

    public function GetResult()
    {
        return $this->builder->GetResult();
    }
}

Jegyzetek

[szerkesztés]
  1. Index of /archive/2010/winter/51023-1/presentations. www.classes.cs.uchicago.edu . (Hozzáférés: 2016. március 3.)

További információk

[szerkesztés]
Az angol Wikikönyvekben
további információk találhatók

Fordítás

[szerkesztés]

Ez a szócikk részben vagy egészben a Builder pattern című angol Wikipédia-szócikk fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.