2011. január 27., csütörtök

Építő (Builder)

Egy újabb olyan minta, ami a Factory-ból következik, ám attól picit eltérő a felhasználási módja. Az Építő (Builder) célja az, hogy a megfelelő elemeket menet közben építse fel különböző ábrázolási módszerekkel. Egy összetett objektum szerkezetének függetlenítése az ábrázolásától, így ugyanazzal az építési folyamattal különböző ábrázolásokat hozhatunk létre.

Motiváció

Egy RTF (Rich Text Format) dokumentumcsere formátum olvasónak képesnek kell lennie az RTF számos egyéb szövegformátumba való konvertálásra. Az olvasó átalakíthatja sima ASCII szöveggé vagy egy interaktívan szerkeszthető szövegeszközzé. A probléma mégis az, hogy a lehetséges konverziók száma végtelen. Így egyszerűen, az olvasó változtatása nélkül kell az új átalakítást megadni.
Egy megoldás, ha az RTFReader osztályt egy más szövegmegjelenítésre konvertáló TextConverter objektummal állítjuk be. Amikor az RTFReader elemzi az RTF dokumentumot, a TextConvertert használja az átalakítás végrehajtására. Amikor az RTFReader felismer egy RTF tokent (sima szöveget vagy RTF vezérlő szót), kiad egy kérelmet a TextConverter-nek, hogy alakítsa át az adott tokent. A TextConverter objektumok felelnek az adatkonverzióért és a token egy bizonyos megjelenítési formátumáért.
A TextConverter alosztályai specializálódhatnak különböző konverziókra és formátumokra. Például a az ASCIIConverter elutasíthat minden konverziós felkérést, kivéve a sima szövegeseket. A TeXConverter megvalósíthatja azokat a műveleteket, amelyek a TeX formátumban való, a szöveg minden stílusinformációját tartalmazó megjelenítésre vonatkozó kérelmekkel kapcsolatosak. A TextWidgetConverter olyan felhasználói felületet hozhat létre, amivel a felhasználó láthatja és szerkesztheti a szöveget.
Mindegyik konvertáló osztály-típus tartalmazza az összetett objektumok létrehozásához és összeállításához szükséges mechanizmusokat, és ezeket egy absztrakt interfész mögé rejti. Az átalakító független az olvasótól, amely az RTF dokumentum elemzéséért felelős.
A Builder minta magába foglalja mindezen kapcsolatokat. A mintában minden konverter osztályt builder-nek, és minden olvasót director-nak hívnak. A példát alkalmazva, a Builder minta elkülöníti a szöveges formátumot értelmező algoritmust (ez az, ami az RTF dokumentumokat elemezi) az átalakított formátum létrehozásától és megjelenítésétől. Lehetővé teszi számunkra, hogy az RTFReader elemző algoritmusát újra felhasználjuk különböző szövegmegjelenítés létrehozására RTF dokumentumokból – csak más TextConverter alosztályt kell beállítani az RTFReader számára.

Alkalmazhatóság

Akkor alkalmazzuk a Builder mintát, ha
  • Az összetett objektum létrehozási algoritmusának függetlennek kell lennie az objektumot felépítő részektől és azok összeállítási módjától.
  • A létrehozási folyamatnak lehetővé kell tennie a létrehozott objektum különböző megjelenítését.
Struktúra


Résztvevők

  • Builder (TextConverter) meghatározza a Product objektum létrehozására szolgáló absztrakt interfészt
  • ConcreteBuilder (ASCIIConverter, TeXConverter, TextWidgetConverter) a Builder interfészt megvalósításánál felépíti és összeállítja a termék részeit. Definiálja és nyomon követi az általa készített megjelenítési módokat. Interfészt biztosít a termék beolvasásához (pl.: GetASCIIText, GetTextWidget)
  • Director (RTFReader) a Builder interfész használatával létrehoz egy objektumot.
  • Product (ASCIIText, TeXText, TextWidget) a felépítendő összetett objektumot képviseli. ConcreteBuilder felépíti a termék belső ábrázolását és definiálja az összeállítási folyamatokat. Tartalmazza az alkotó elemeket defináló osztályokat, beleértve a részek összeállítására szolgáló interfészeket.

Együttműködési információk

  • A kliens létrehozza a Director objektumot és beállítja a kívánt Builder objektummal.
  • A Director értesíti a builder-t, ha egy objektum részét fel kell építeni.
  • A Builder kezeli a dircetor-tól érkező kérvényeket és a részeket ad a product-hoz.
  • A kliens elkéri a product-ot a builder-től.


A fenti diagram a Builder és a Director együttműködését szemlélteti a klienssel.

Következmények

A Builder minta föbb következményei:
  1. Lehetővé teszi a termék belső ábrázolásának a megváltoztatását. A Builder objektum a director számára egy absztrakt interfészt biztosít a termék felépítéséhez. Az interfész lehetővé teszi a builder-nek, hogy elrejtse a termék ábrázolását és belső felépítését. Elrejti a termék összeállításának módját is. Mivel a termék felépítése egy absztrakt interfészen keresztül történik, elég a termék belső ábrázolását megváltoztatni egy új builder típus definiálásához.
  2. Elszigeteli a felépítési és az összeállítási kódot. A Builder minta a modularitást az összetett objektum felépítésének és megjelenítésének az egységbe zárásával javítja. A kliensnek nem kell tudnia semmit a termék belső szerkezetét definiáló osztályokról; ilyen osztályok nem jelennek meg a Builder interfészében.
  3. Az építési folyamat finomabb vezérlését teszi lehetővé. Azoktól a létrehozási mintáktól eltérően, amelyek egy lépésben építik fel a terméket, a Builder minta lépésről lépésre építi fel a terméket a director felügyelete mellett. Csak akkor veszi át a director a terméket a builder-től, ha az elkészült vele. Emiatt a Builder interfész jobban tükrözi a termék felépítésének folyamatát, mint a többi létrehozási minta. Finomabb vezérlést tesz lehetővé az építési folyamatokon és ennek következtében a végső termék belső szerkezetén.

Implementációs rész

Tipikusan van egy absztrakt Builder osztály, ami minden egyes komponens működését definiálja, amelyek létrehozására a director megkéri. Alapértelmezésben a műveletek nem tesznek semmit. A ConcreteBuilder osztály felülbírálja azon komponensek műveleteit, amelyeknek létrehozásában érdekelt.
  1. Összeállítási és építési interfész. A Builder-ek termékeiket lépésről lépésre készítik el. Emiatt a Builder osztály interfésznek elég általánosnak kell lennie, hogy lehetővé tegye mindenfajta konkrét builder-nek a termékek létrehozását. A fő tervezési probléma az építési- és összeállítási folyamatok modellje. Azok a modellek megfelelőek, amelyek a létrehozási kérelmek eredményeit egyszerűen hozzácsatolják a termékhez.
  2. Miért nem absztrakt osztályokat használunk a termékeknél? Általában a konkrét builder-ek termékei annyira eltérnek a reprezentációjukban, hogy csak keveset nyerünk azzal, ha egy általános szülőosztályt adunk a különböző termékekhez. Mivel rendszerint a kliens a megfelelő konkrét builder-rel állítja be a director-t, ezért tudja, hogy a Builder melyik konkrét alosztálya van használatban, és ennek megfelelően kezeli a termékeket.
  3. Üres metódusok alapértelmezésként a Builderben. Üres metódusokat definiálunk, hogy a kliensek csak azokat a műveleteket bírálják felül, amelyekben érdekeltek.

Minta kód

A kódban szereplő osztályok:
  • Builder: VehicleBuilder
  • ConcreteBuilder: MotorCycleBuilder, CarBuilder, ScooterBuilder
  • Director: Shop
  • Product: Vehicle
Megvalósítás:
// Builder pattern -- Real World example

using System;
using System.Collections.Generic;

namespace Builder
{
///
/// Builder Design Pattern.
///

public class MainApp
{
///
/// Entry point into console application.
///

public static void Main()
{
VehicleBuilder builder;

// Create shop with vehicle builders
Shop shop = new Shop();

// Construct and display vehicles
builder = new ScooterBuilder();
shop.Construct(builder);
builder.Vehicle.Show();

builder = new CarBuilder();
shop.Construct(builder);
builder.Vehicle.Show();

builder = new MotorCycleBuilder();
shop.Construct(builder);
builder.Vehicle.Show();

// Wait for user
Console.ReadKey();
}
}

///
/// The 'Director' class
///

class Shop
{
// Builder uses a complex series of steps
public void Construct(VehicleBuilder vehicleBuilder)
{
vehicleBuilder.BuildFrame();
vehicleBuilder.BuildEngine();
vehicleBuilder.BuildWheels();
vehicleBuilder.BuildDoors();
}
}

///
/// The 'Builder' abstract class
///

abstract class VehicleBuilder
{
protected Vehicle vehicle;

// Gets vehicle instance
public Vehicle Vehicle
{
get { return vehicle; }
}

// Abstract build methods
public abstract void BuildFrame();
public abstract void BuildEngine();
public abstract void BuildWheels();
public abstract void BuildDoors();
}

///
/// The 'ConcreteBuilder1' class
///

class MotorCycleBuilder : VehicleBuilder
{
public MotorCycleBuilder()
{
vehicle = new Vehicle("MotorCycle");
}

public override void BuildFrame()
{
vehicle["frame"] = "MotorCycle Frame";
}

public override void BuildEngine()
{
vehicle["engine"] = "500 cc";
}

public override void BuildWheels()
{
vehicle["wheels"] = "2";
}

public override void BuildDoors()
{
vehicle["doors"] = "0";
}
}

///
/// The 'ConcreteBuilder2' class
///

class CarBuilder : VehicleBuilder
{
public CarBuilder()
{
vehicle = new Vehicle("Car");
}

public override void BuildFrame()
{
vehicle["frame"] = "Car Frame";
}

public override void BuildEngine()
{
vehicle["engine"] = "2500 cc";
}

public override void BuildWheels()
{
vehicle["wheels"] = "4";
}

public override void BuildDoors()
{
vehicle["doors"] = "4";
}
}

///
/// The 'ConcreteBuilder3' class
///

class ScooterBuilder : VehicleBuilder
{
public ScooterBuilder()
{
vehicle = new Vehicle("Scooter");
}

public override void BuildFrame()
{
vehicle["frame"] = "Scooter Frame";
}

public override void BuildEngine()
{
vehicle["engine"] = "50 cc";
}

public override void BuildWheels()
{
vehicle["wheels"] = "2";
}

public override void BuildDoors()
{
vehicle["doors"] = "0";
}
}

///
/// The 'Product' class
///

class Vehicle
{
private string _vehicleType;
private Dictionary _parts = new Dictionary();

// Constructor
public Vehicle(string vehicleType)
{
this._vehicleType = vehicleType;
}

// Indexer
public string this[string key]
{
get { return _parts[key]; }
set { _parts[key] = value; }
}

public void Show()
{
Console.WriteLine("\n---------------------------");
Console.WriteLine("Vehicle Type: {0}", _vehicleType);
Console.WriteLine(" Frame : {0}", _parts["frame"]);
Console.WriteLine(" Engine : {0}", _parts["engine"]);
Console.WriteLine(" #Wheels: {0}", _parts["wheels"]);
Console.WriteLine(" #Doors : {0}", _parts["doors"]);
}
}
}
Eredmény:


Ismert felhasználás

Különféle formátumok konvertálásához, fordítókban elemzőknél, normalizált adatbázisok „építéséhez”.

Kapcsolódó minták

Az Abstract Factory abban hasonlít a Builder-re, hogy az is összetett objektumokat építhet fel. Az elsődleges különbség, hogy a Builder minta az összetett objektumok lépésről lépésre való létrehozását helyezi középpontba. Az Abstract Factory esetében a termékobjektum-családok a hangsúlyosabbak (akár egyszerű, akár összetett). A Builder minta utolsó lépésben visszaadja a terméket, azonban az Abstract Factory minta, ami azt illeti azonnal.

Felhasznált anyagok

0 megjegyzés :

Megjegyzés küldése