Budowniczy – Wzorzec Projektowy (Builder)

Budowniczy – Wzorzec Projektowy (ang. Builder). Przykład zastosowania oparty na historii w celu łatwiejszego zrozumienia tego wzorca dla laika.

James miał mała firmę. Składał i sprzedawał hulajnogi w wersji standardowej. Taki pojazd składa się z hamulca o nazwie HamulceStd, kierownicy – KierownicaStd, kółek – KolkaStd i naklejki z nazwa hulajnogi – Standard.

public class HulajnogaStandardowa
{
	public string Hamulec { get; set; }
	public string Kierownica { get; set; }
	public string Kolka { get; set; }
	public string Naklejka { get; set; }

	public void StworzHulajnoge(string hamulec, string kierownica, string kolka, string naklejka)
	{
		Hamulec = hamulec;
		Kierownica = kierownica;
		Kolka = kolka;
		Naklejka = naklejka;
	}
}

Budowa nie była skomplikowana:

 
var hulajnogaStandardowa = new HulajnogaStandardowa();
hulajnogaStandardowa.StworzHulajnoge("HamulceStd", "KierownicaStd", "KolkaStd", "Standard");

James dobrze radził sobie w trudnych czasach kapitalizmu. Pewnego razu jego firma wygrała przetarg na 1000 standardowych hulajnóg. Postanowił więc zatrudnić swojego brata Arona do pomocy. James stopniowo pokazywał Aronowi jak montować kółka, hamulce, kierownice i jak poprawnie nakleić naklejkę. Aron stał się rzemieślnikiem standardowych hulajnóg (konkretnym budowniczym). Aron nauczył się pewnego schematu, wpierw dokręcić kółka, później zamontować hamulce, dokleić naklejkę, a na koniec zamontować kierownice.

 
public class HulajnogaStandardowa
{
	public string Hamulec { get; set; }
	public string Kierownica { get; set; }
	public string Kolka { get; set; }
	public string Naklejka { get; set; }

	public void DokrecKolka()
	{
		Kolka = "KolkaStd";
	}

	public void ZamontujHamulce()
	{
		Hamulec = "HamulceStd";
	}

	public void DoklejNaklejke()
	{
		Naklejka = "Standard";
	}

	public void ZamontujKierownice()
	{
		Kierownica = "KierownicaStd";
	}
}

W momencie kiedy Aron budował dwukołowe pojazdy, James (już poważny dyrektor firmy) zajmował się liczeniem pieniędzy.

 
var hulajnogaStandardowa = new HulajnogaStandardowa();
hulajnogaStandardowa.DokrecKolka();
hulajnogaStandardowa.ZamontujHamulce();
hulajnogaStandardowa.DoklejNaklejke();
hulajnogaStandardowa.ZamontujKierownice();

James po kilka miesiącach testów postanowił wprowadzić nowy model hulajnogi – wersje Extreme do jazdy wyczynowej. Wersja Extreme miała jednak całkowicie inne komponenty. James postanowił zatrudnić kolegę Michała do budowania hulajnogi Extreme. Michał został wyszkolony przez James i Arona jak szybko i sprawnie budować nową wersję hulajnogi.

 
public class HulajnogaExtreme
{
  public string Hamulec { get; set; }
  public string Kierownica { get; set; }
  public string Kolka { get; set; }
  public string Naklejka { get; set; }

  public void DokrecKolka()
  {
    Kolka = "KolkaExtreme";
  }

  public void ZamontujHamulce()
  {
    Hamulec = "HamulceExtreme";
  }

  public void DoklejNaklejke()
  {
    Naklejka = "Extreme";
  }

  public void ZamontujKierownice()
  {
    Kierownica = "KierownicaExtreme";
  }
}

Sposób budowy był taki sam jak w przypadku standardowej hulajnogi. Michał stał się mistrzem (konkretnym budowniczym) w budowaniu extremalnej hulajnogi.

 
var hulajnogaExtreme = new HulajnogaExtreme();
hulajnogaExtreme.DokrecKolka();
hulajnogaExtreme.ZamontujHamulce();
hulajnogaExtreme.DoklejNaklejke();
hulajnogaExtreme.ZamontujKierownice();

Czy dostrzegasz jakieś problemy?
Hulajnoga sama sobie nie dokręci kółek, nie zamontuje hamulców czy kierownicy. Robią to ludzie. Aron jest konkretnym budowniczym standardowych hulajnóg, a Michał wersji Extreme. Dodajmy do programu naszych rzemieślników budowlanych.

 
public class HulajnogaStandardowa
{
	public string Hamulec { get; set; }
	public string Kierownica { get; set; }
	public string Kolka { get; set; }
	public string Naklejka { get; set; }
}

public class HulajnogaExtreme
{
	public string Hamulec { get; set; }
	public string Kierownica { get; set; }
	public string Kolka { get; set; }
	public string Naklejka { get; set; }
}

//Aron jako
public class BudownczyHulajnogiStandardowej
{
	private Hulajnoga _hulajnogaStandardowa = new HulajnogaStandardowa();

	public void DokrecKolka()
	{
		_hulajnogaStandardowa.Kolka = "KolkaStd";
	}

	public void ZamontujHamulce()
	{
		_hulajnogaStandardowa.Hamulec = "HamulceStd";
	}

	public void DoklejNaklejke()
	{
		_hulajnogaStandardowa.Naklejka = "Standard";
	}

	public void ZamontujKierownice()
	{
		_hulajnogaStandardowa.Kierownica = "KierownicaStd";
	}
}

// Michał jako
public class BudowniczyHulajnogiExtremalnej
{
	private HulajnogaExtreme _hulajnogaExtreme = new HulajnogaExtreme();

	public void DokrecKolka()
	{
		_hulajnogaExtreme.Kolka = "KolkaExtreme";
	}

	public void ZamontujHamulce()
	{
		_hulajnogaExtreme.Hamulec = "HamulceExtreme";
	}

	public void DoklejNaklejke()
	{
		_hulajnogaExtreme.Naklejka = "Extreme";
	}

	public void ZamontujKierownice()
	{
		_hulajnogaExtreme.Kierownica = "KierownicaExtreme";
	}
}

Dwie klasy: HulajnogaStandardowa oraz HulajnogaExtreme maja te same właściwości. Stwórzmy wiec dla nich jedna klasę.

 
public class Hulajnoga
{
	public string Hamulec { get; set; }
	public string Kierownica { get; set; }
	public string Kolka { get; set; }
	public string Naklejka { get; set; }

	public void PokazHulajnoge()
	{
		var informacjeOHulajnodze = String.Format("Hulajnoga w wersji: {0}.nKierownica: {1}.nKola: {2}.nHamulec {3}.", Naklejka, Kierownica, Kolka, Hamulec);
		Console.WriteLine(informacjeOHulajnodze);
	}
}

public class BudownczyHulajnogiStandardowej
{
	private Hulajnoga _hulajnoga = new Hulajnoga();
	...
 }

public class BudowniczyHulajnogiExtremalnej
{
	private Hulajnoga _hulajnoga = new Hulajnoga();
	...
}

Zauważ, ze BudownczyHulajnogiStandardowej oraz BudowniczyHulajnogiExtremalnej maja te same metody. Stwórzmy więc dla tych klas wspólny interfejs:

 
public interface IBudowniczyHulajnog
{
	void DokrecKolka();
	void ZamontujHamulce();
	void DoklejNaklejke();
	void ZamontujKierownice();
	Hulajnoga OddajZbudowanaHulajnoge();
}


public class BudownczyHulajnogiStandardowej : IBudowniczyHulajnog
{
    ....
}
public class BudowniczyHulajnogiExtremalnej : IBudowniczyHulajnog
{
    ....
}

Dodaliśmy również metodę OddajZbudowanaHulajnoge. W momencie w którym któryś z konkretnych budowniczych zbuduje hulajnogę to powinien ten pojazd oddać dyrektorowi. Apropo dyrektora. Dodajmy go do naszego programu. A co może robić dyrektor? Poprosić swojego pracownika, aby stworzył dla niego hulajnogę w sposób jaki dyrektor uważa za właściwy.

 
public class Dyrektor
{
	public void ZbudujMiHulajnogeWKtorejJestesSpecjalista(IBudowniczyHulajnog budowniczy)
	{
		budowniczy.DokrecKolka();
		budowniczy.ZamontujHamulce();
		budowniczy.DoklejNaklejke();
		budowniczy.ZamontujKierownice();
	}
}

Zobaczmy więc jak firma może teraz działać:

 
class Program
{
	static void Main(string[] args)
	{
		Dyrektor James = new Dyrektor();
		IBudowniczyHulajnog Aron = new BudownczyHulajnogiStandardowej();
		IBudowniczyHulajnog Michal = new BudowniczyHulajnogiExtremalnej();

		James.ZbudujMiHulajnogeWKtorejJestesSpecjalista(Aron);
		James.ZbudujMiHulajnogeWKtorejJestesSpecjalista(Michal);

		Hulajnoga hulajnogaStandardowa = Aron.OddajZbudowanaHulajnogeSzefowi();
		Hulajnoga hulajnogaExtreme = Michal.OddajZbudowanaHulajnogeSzefowi();

		hulajnogaStandardowa.PokazHulajnoge();
		Console.WriteLine();
		hulajnogaExtreme.PokazHulajnoge();
		Console.ReadKey();
	}
}

Trochę teorii:

Poniższy diagram UML przedstawia wzorzec projektowy Budowniczy.

Wzorzec Projektowy: Budowniczy
Źródło: wikipedia.org

Odnieśmy ten diagram UML do naszego przykładu.

Dyrektor jako Director
IBudowniczyHulajnog jako Builder
BudownczyHulajnogiStandardowej jako ConcreteBuilder
BudowniczyHulajnogiExtremalnej jako ConcreteBuilder
Hulajnoga jako Product

Wzorzec ten został opisany przez Bandę Czworga (Gang of Four). Wzorzec projektowy Budowniczy należy do wzorców kreacyjnych. Idea jaka stoi za tym wzorem jest prosta. Tworzeniem tych samych obiektów (Product), ale o innych właściwościach zajmują się wyspecjalizowane w tym temacie klasy zwane ConcreteBuilder. ConcreteBuilder implementują interfejs Builder. Klasy ConcreteBuilder są zaprzęgane do działania dzięki klasie Dyrektor.

Przydatne linki:

CodeProject – przykład w C# (ang)
DoFactory – przykład w C# (ang)
Dotnet-tricks – przykład w C# (ang)
Wikipedia (ang)
Wikipedia (pl)

Leave a Comment

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *