Wzorzec projektowy budowniczy - builder

Stronę tą wyświetlono już: 12 razy

Opis wzorca

Wzorzec budowniczy (ang. builder) należy do grupy wzorców kreacyjnych. Jego zadaniem jest za pomocą interfejsu budowniczego i klasy dyrektora budowanie konkretnych typów obiektów. Dyrektor dysponuje wskaźnikiem na interfejs budowniczego, za którym stoi klasa budowniczego konkretnego typu obiektu. Wskaźnik ten jest ustawiany za pomocą metody setBuilder, która przyjmuje jako jedyny argument wskaźnik na interfejs budowniczego.

Model ten można zastosować w grze komputerowej albowiem łatwo sobie wyobrazić np. budynek, którego celem jest wytwarzanie konkretnych obiektów, na które składają się obiekty składowe. Takim budynkiem może być np. fabryka różnego typu czołgów, ludzi, samolotów czy też innych obiektów często spotykanych w grach strategicznych. Użytkownik sobie wybiera obiekty, które mają zostać wyprodukowane a te w postaci wskaźników na interfejsy konkretnych budowniczych są odkładane na stertę. Z sterty kolejno zdejmowane są interfejsy i po wykonaniu danego obiektu pobierany jest kolejny wskaźnik na interfejs budowniczego aż na stosie nie będzie już interfejsów do obsłużenia (wykonane zostały już wszystkie obiekty z zamówienia idącego od użytkownika).

Przykład diagramu UML wzorca projektowego budowniczy

Sercem wzorca budowniczego z poniższego diagramu UML jest interfejs iBuilderTank oraz klasy:

  • Tank zawierająca opis obiektu budowanego;
  • BuilderTankA1M1_Abrams udostępniająca metody budowy konkretnego modelu czołgu (w tym przypadku amerykańskiego czołgu z pancerzez z zubożonego uranu A1M1 Abrams);
  • BuilderTankT34 udostępniająca metody budowy konkretnego modelu czołgu (w tym przypadku rosyjskiego czołgu T34)

Jak widać na poniższym diagramie klasa Tank agreguje obiekty klas:

  • Armor - opisującej opancerzenie czołgu;
  • Cannon - opisującej działo czołgu;
  • Chassis - opisującej podwozie czołgu;
  • FuelTank - opisującej zbiornik paliwa czołgu;
  • Engine - opisującej silnik czołgu

Jak widać interfejs iBuilderTank jest agregowany przez klasę nadzorcy (dyrektora) TankDirector, do której za pośrednictwem metody setBuilder wskaźnik na tenże interfejs jest przekazywany. Metoda createTank przygotowuje czołg budując poszczególne jego elementy i składając go do kupy. Po wybudowaniu można wyjechać z fabryki nowiusieńkim czołgiem uzyskując nowy obiekt klasy Tank za pomocą metody getTank.

Przykładowy diagram UML dla wzorca projektowego budowniczy
Rys. 1
Przykładowy diagram UML dla wzorca projektowego budowniczy

Implementacja wzorca w C++

Oto przykładowa implementacja wzorca budowniczego, która opisana została na powyższym diagramie UML:

Listing 1
  1. #include <iostream>
  2. #include <string>
  3. class iDescription{
  4. public:
  5. virtual void writeDescription() = 0;
  6. };
  7. class Engine : public iDescription{
  8. private:
  9. float mass;
  10. float power;
  11. float fuelPerKilometer;
  12. public:
  13. inline Engine(float mass, float power, float fuelPerKilometer) : mass(mass), power(power), fuelPerKilometer(fuelPerKilometer){};
  14. inline float getMass(){ return mass; };
  15. inline float getPower(){ return power; };
  16. inline float getFuelPerKilometer() { return fuelPerKilometer; };
  17. void writeDescription(){
  18. std::cout<<"Masa silnika: "<<mass<<" [kg]\n";
  19. std::cout<<"Moc silnika: "<<power<<" [KM]\n";
  20. std::cout<<"Zuzycie paliwa: "<<fuelPerKilometer<<" [litr/100 km]\n";
  21. }
  22. inline void setMass(float mass){
  23. if(mass > 0){
  24. this->mass = mass;
  25. }
  26. }
  27. inline void setPower(float power){
  28. if(power > 0){
  29. this->power = power;
  30. }
  31. }
  32. inline void setFuelPerKilometer(float fuelPerKilometer){
  33. if(fuelPerKilometer > 0){
  34. this->fuelPerKilometer = fuelPerKilometer;
  35. }
  36. };
  37. };
  38. class Armor : public iDescription{
  39. private:
  40. float mass;
  41. int health;
  42. public:
  43. inline Armor(float mass, int health) : mass(mass), health(health) {};
  44. inline float getMass(){ return mass; };
  45. inline int getHealth(){ return health; };
  46. void writeDescription(){
  47. std::cout<<"Masa zbroi: "<<mass<<" [kg]\n";
  48. std::cout<<"Wytrzymalosc zbroi: "<<health<<" [punkty wytrzymalosci]\n";
  49. }
  50. void setMass(float mass){
  51. if(mass > 0){
  52. this->mass = mass;
  53. }
  54. };
  55. void setHelth(int health){
  56. if(health >= 0){
  57. this->health = health;
  58. }
  59. };
  60. };
  61. class Cannon : public iDescription{
  62. private:
  63. float diameter;
  64. float mass;
  65. float maxDistance;
  66. public:
  67. inline Cannon(float diameter, float mass, float maxDistance) :diameter(diameter), mass(mass), maxDistance(maxDistance){};
  68. inline float getDiameter(){ return diameter; };
  69. inline float getMass(){ return mass; };
  70. inline float getMaxDistance(){ return maxDistance; };
  71. void writeDescription(){
  72. std::cout<<"Srednica dziala: "<<diameter<<" [mm]\n";
  73. std::cout<<"Masa dziala: "<<mass<<" [kg]\n";
  74. std::cout<<"Maksymalny promien razenia dziala: "<<maxDistance<<" [km]\n";
  75. }
  76. void setDiameter(float diameter){
  77. this->diameter = diameter;
  78. }
  79. void setMass(float mass){
  80. this->mass = mass;
  81. }
  82. void setMaxDistance(float maxDistance){
  83. this->maxDistance = maxDistance;
  84. }
  85. };
  86. class Chassis : public iDescription{
  87. private:
  88. float mass;
  89. std::string type;
  90. public:
  91. inline Chassis(float mass, std::string type) :mass(mass), type(type){};
  92. inline float getMass(){ return mass; };
  93. inline std::string getType(){ return type; };
  94. inline void setMass(float mass){
  95. if(mass > 0){
  96. this->mass = mass;
  97. };
  98. };
  99. inline void setType(std::string type){ this->type = type; };
  100. void writeDescription(){
  101. std::cout<<"Masa podwozia: "<<mass<<" [kg]\n";
  102. std::cout<<"Typ podwozia: "<<type<<"\n";
  103. }
  104. };
  105. class FuelTank : public iDescription{
  106. private:
  107. float maximum;
  108. float fuelLevel;
  109. public:
  110. inline FuelTank(float maximum, float fuelLevel):maximum(maximum), fuelLevel(fuelLevel){};
  111. inline float getMaximum(){ return maximum; };
  112. inline float getFuelLevel(){ return fuelLevel * maximum; };
  113. void writeDescription(){
  114. std::cout<<"Maksymalna pojemnosc zbiornika paliwa: "<<maximum<<"\n";
  115. std::cout<<"Poziom paliwa w zbiorniku paliwa: "<<fuelLevel * maximum<<"\n";
  116. };
  117. void setMaximum(float maximum){
  118. if(maximum > 0){
  119. this->maximum = maximum;
  120. }
  121. };
  122. void setFuelLevel(float fuelLevel){
  123. if(fuelLevel >= 0){
  124. this->fuelLevel = fuelLevel;
  125. }
  126. };
  127. };
  128. class Tank : public iDescription{
  129. protected:
  130. std::string tankType;
  131. FuelTank *fuelTank;
  132. Chassis *chassis;
  133. Cannon *cannon;
  134. Armor *armor;
  135. Engine *engine;
  136. public:
  137. inline Tank() : fuelTank(NULL), chassis(NULL), cannon(NULL), armor(NULL), engine(NULL) {};
  138. ~Tank(){
  139. if(fuelTank)
  140. delete fuelTank;
  141. if(chassis)
  142. delete chassis;
  143. if(cannon)
  144. delete cannon;
  145. if(armor)
  146. delete armor;
  147. if(engine)
  148. delete engine;
  149. }
  150. void writeDescription(){
  151. std::cout<<"Typ czolgu: "<<tankType<<"\n";
  152. if(chassis){
  153. chassis->writeDescription();
  154. }else{
  155. std::cout<<"Czolg nie ma jeszcze podwozia\n";
  156. }
  157. if(cannon){
  158. cannon->writeDescription();
  159. }else{
  160. std::cout<<"Czolg nie ma zamontowanego jeszcze dziala\n";
  161. }
  162. if(armor){
  163. armor->writeDescription();
  164. }else{
  165. std::cout<<"Czolg nie ma zamontowanego opancerzenia\n";
  166. }
  167. if(engine){
  168. engine->writeDescription();
  169. }else{
  170. std::cout<<"Czolg nie ma zamontowanego jeszcze silnika\n";
  171. }
  172. if(fuelTank){
  173. fuelTank->writeDescription();
  174. }else{
  175. std::cout<<"Czolg nie ma zamontowanego jeszcze zbiornika paliwa\n";
  176. }
  177. }
  178. void setTankType(std::string tankType){
  179. this->tankType = tankType;
  180. }
  181. void setChassis(float mass, std::string type){
  182. if(!chassis){
  183. chassis = new Chassis(mass, type);
  184. }else{
  185. chassis->setMass(mass);
  186. chassis->setType(type);
  187. }
  188. }
  189. void setCannon(float diameter, float mass, float maxDistance){
  190. if(!cannon){
  191. cannon = new Cannon(diameter, mass, maxDistance);
  192. }else{
  193. cannon->setDiameter(diameter);
  194. cannon->setMass(mass);
  195. cannon->setMaxDistance(maxDistance);
  196. }
  197. }
  198. void setArmor(float mass, int health){
  199. if(!armor){
  200. armor = new Armor(mass, health);
  201. }else{
  202. armor->setMass(mass);
  203. armor->setHelth(health);
  204. }
  205. }
  206. void setEngine(float mass, float power, float fuelPerKilometer){
  207. if(!engine){
  208. engine = new Engine(mass, power, fuelPerKilometer);
  209. }else{
  210. engine->setMass(mass);
  211. engine->setPower(power);
  212. engine->setFuelPerKilometer(fuelPerKilometer);
  213. }
  214. }
  215. void setFuelTank(float maximum, float fuelLevel){
  216. if(!fuelTank){
  217. fuelTank = new FuelTank(maximum, fuelLevel);
  218. }else{
  219. fuelTank->setMaximum(maximum);
  220. fuelTank->setFuelLevel(fuelLevel);
  221. }
  222. }
  223. };
  224. class iBuilderTank{
  225. protected:
  226. Tank *tank;
  227. public:
  228. virtual void newTank() = 0;
  229. inline Tank* getTank(){
  230. std::cout<<"Biez czolg, znikaj i po polu fikaj!!!";
  231. return tank;
  232. }
  233. virtual void buildChassis() = 0;
  234. virtual void buildCannon() = 0;
  235. virtual void buildArmor() = 0;
  236. virtual void buildEngine() = 0;
  237. virtual void buildFuelTank() = 0;
  238. };
  239. // A1M1 Abrams
  240. class BuilderTankA1M1_Abrams : public iBuilderTank{
  241. public:
  242. void newTank(){
  243. tank = new Tank();
  244. tank->setTankType("A1M1 Abrams");
  245. std::cout<<"Tworze czolg\n\n";
  246. tank->writeDescription();
  247. };
  248. void buildChassis(){
  249. std::cout<<"\nBuduje podwozie czolgu:\n";
  250. tank->setChassis(5000, "gasienicowy");
  251. std::cout<<"Proces ukonczony\n";
  252. tank->writeDescription();
  253. };
  254. void buildCannon(){
  255. std::cout<<"\nBuduje dzialko czolgu:\n";
  256. tank->setCannon(105, 10000, 4);
  257. std::cout<<"Proces ukonczony\n";
  258. tank->writeDescription();
  259. };
  260. void buildArmor(){
  261. std::cout<<"\nBuduje pancerz czolgu:\n";
  262. tank->setArmor(30000, 3000);
  263. std::cout<<"\nProces ukonczony\n";
  264. tank->writeDescription();
  265. };
  266. void buildEngine(){
  267. std::cout<<"\nBuduje silnik czolgu:\n";
  268. tank->setEngine(500, 1335, 37);
  269. std::cout<<"Proces ukonczony\n";
  270. tank->writeDescription();
  271. };
  272. void buildFuelTank(){
  273. std::cout<<"\nBuduje zbiornik paliwa czolgu:\n";
  274. tank->setFuelTank(1900, 1);
  275. std::cout<<"Proces ukonczony\n";
  276. tank->writeDescription();
  277. };
  278. };
  279. class BuilderTankT34 : public iBuilderTank{
  280. public:
  281. void newTank(){
  282. tank = new Tank();
  283. tank->setTankType("T34");
  284. std::cout<<"Tworze czolg\n\n";
  285. tank->writeDescription();
  286. };
  287. void buildChassis(){
  288. std::cout<<"\nBuduje podwozie czolgu:\n";
  289. tank->setChassis(3000, "gasienicowy");
  290. std::cout<<"Proces ukonczony\n";
  291. tank->writeDescription();
  292. };
  293. void buildCannon(){
  294. std::cout<<"\nBuduje dzialko czolgu:\n";
  295. tank->setCannon(76.2, 5000, 3);
  296. std::cout<<"Proces ukonczony\n";
  297. tank->writeDescription();
  298. };
  299. void buildArmor(){
  300. std::cout<<"\nBuduje pancerz czolgu:\n";
  301. tank->setArmor(15000, 1000);
  302. std::cout<<"\nProces ukonczony\n";
  303. tank->writeDescription();
  304. };
  305. void buildEngine(){
  306. std::cout<<"\nBuduje silnik czolgu:\n";
  307. tank->setEngine(500, 500, 100);
  308. std::cout<<"Proces ukonczony\n";
  309. tank->writeDescription();
  310. };
  311. void buildFuelTank(){
  312. std::cout<<"\nBuduje zbiornik paliwa czolgu:\n";
  313. tank->setFuelTank(480, 1);
  314. std::cout<<"Proces ukonczony\n";
  315. tank->writeDescription();
  316. };
  317. };
  318. class TankDirector{
  319. private:
  320. iBuilderTank* builder;
  321. public:
  322. inline void setBuilder(iBuilderTank* builder){
  323. this->builder = builder;
  324. }
  325. Tank* getTank(){
  326. if(builder){
  327. return builder->getTank();
  328. }
  329. return NULL;
  330. }
  331. void createTank(){
  332. builder->newTank();
  333. builder->buildChassis();
  334. builder->buildCannon();
  335. builder->buildArmor();
  336. builder->buildEngine();
  337. builder->buildFuelTank();
  338. std::cout<<"\n\nCzolg zlozony, ukonczony!!!\n\n";
  339. }
  340. };
  341. int main(){
  342. BuilderTankT34 buildT34;
  343. BuilderTankA1M1_Abrams buildA1M1;
  344. TankDirector tankDirector;
  345. tankDirector.setBuilder(&buildT34);
  346. tankDirector.createTank();
  347. Tank* t34 = tankDirector.getTank();
  348. std::cout<<"\n\n";
  349. tankDirector.setBuilder(&buildA1M1);
  350. tankDirector.createTank();
  351. Tank* a1m1 = tankDirector.getTank();
  352. delete t34;
  353. std::cin.get();
  354. return 0;
  355. }

Wynik działania powyższego kodu:

Tworze czolg

Typ czolgu: T34
Czolg nie ma jeszcze podwozia
Czolg nie ma zamontowanego jeszcze dziala
Czolg nie ma zamontowanego opancerzenia
Czolg nie ma zamontowanego jeszcze silnika
Czolg nie ma zamontowanego jeszcze zbiornika paliwa

Buduje podwozie czolgu:
Proces ukonczony
Typ czolgu: T34
Masa podwozia: 3000 [kg]
Typ podwozia: gasienicowy
Czolg nie ma zamontowanego jeszcze dziala
Czolg nie ma zamontowanego opancerzenia
Czolg nie ma zamontowanego jeszcze silnika
Czolg nie ma zamontowanego jeszcze zbiornika paliwa

Buduje dzialko czolgu:
Proces ukonczony
Typ czolgu: T34
Masa podwozia: 3000 [kg]
Typ podwozia: gasienicowy
Srednica dziala: 76.2 [mm]
Masa dziala: 5000 [kg]
Maksymalny promien razenia dziala: 3 [km]
Czolg nie ma zamontowanego opancerzenia
Czolg nie ma zamontowanego jeszcze silnika
Czolg nie ma zamontowanego jeszcze zbiornika paliwa

Buduje pancerz czolgu:

Proces ukonczony
Typ czolgu: T34
Masa podwozia: 3000 [kg]
Typ podwozia: gasienicowy
Srednica dziala: 76.2 [mm]
Masa dziala: 5000 [kg]
Maksymalny promien razenia dziala: 3 [km]
Masa zbroi: 15000 [kg]
Wytrzymalosc zbroi: 1000 [punkty wytrzymalosci]
Czolg nie ma zamontowanego jeszcze silnika
Czolg nie ma zamontowanego jeszcze zbiornika paliwa

Buduje silnik czolgu:
Proces ukonczony
Typ czolgu: T34
Masa podwozia: 3000 [kg]
Typ podwozia: gasienicowy
Srednica dziala: 76.2 [mm]
Masa dziala: 5000 [kg]
Maksymalny promien razenia dziala: 3 [km]
Masa zbroi: 15000 [kg]
Wytrzymalosc zbroi: 1000 [punkty wytrzymalosci]
Masa silnika: 500 [kg]
Moc silnika: 500 [KM]
Zuzycie paliwa: 100 [litr/100 km]
Czolg nie ma zamontowanego jeszcze zbiornika paliwa

Buduje zbiornik paliwa czolgu:
Proces ukonczony
Typ czolgu: T34
Masa podwozia: 3000 [kg]
Typ podwozia: gasienicowy
Srednica dziala: 76.2 [mm]
Masa dziala: 5000 [kg]
Maksymalny promien razenia dziala: 3 [km]
Masa zbroi: 15000 [kg]
Wytrzymalosc zbroi: 1000 [punkty wytrzymalosci]
Masa silnika: 500 [kg]
Moc silnika: 500 [KM]
Zuzycie paliwa: 100 [litr/100 km]
Maksymalna pojemnosc zbiornika paliwa: 480
Poziom paliwa w zbiorniku paliwa: 480


Czolg zlozony, ukonczony!!!

Biez czolg, znikaj i po polu fikaj!!!

Tworze czolg

Typ czolgu: A1M1 Abrams
Czolg nie ma jeszcze podwozia
Czolg nie ma zamontowanego jeszcze dziala
Czolg nie ma zamontowanego opancerzenia
Czolg nie ma zamontowanego jeszcze silnika
Czolg nie ma zamontowanego jeszcze zbiornika paliwa

Buduje podwozie czolgu:
Proces ukonczony
Typ czolgu: A1M1 Abrams
Masa podwozia: 5000 [kg]
Typ podwozia: gasienicowy
Czolg nie ma zamontowanego jeszcze dziala
Czolg nie ma zamontowanego opancerzenia
Czolg nie ma zamontowanego jeszcze silnika
Czolg nie ma zamontowanego jeszcze zbiornika paliwa

Buduje dzialko czolgu:
Proces ukonczony
Typ czolgu: A1M1 Abrams
Masa podwozia: 5000 [kg]
Typ podwozia: gasienicowy
Srednica dziala: 105 [mm]
Masa dziala: 10000 [kg]
Maksymalny promien razenia dziala: 4 [km]
Czolg nie ma zamontowanego opancerzenia
Czolg nie ma zamontowanego jeszcze silnika
Czolg nie ma zamontowanego jeszcze zbiornika paliwa

Buduje pancerz czolgu:

Proces ukonczony
Typ czolgu: A1M1 Abrams
Masa podwozia: 5000 [kg]
Typ podwozia: gasienicowy
Srednica dziala: 105 [mm]
Masa dziala: 10000 [kg]
Maksymalny promien razenia dziala: 4 [km]
Masa zbroi: 30000 [kg]
Wytrzymalosc zbroi: 3000 [punkty wytrzymalosci]
Czolg nie ma zamontowanego jeszcze silnika
Czolg nie ma zamontowanego jeszcze zbiornika paliwa

Buduje silnik czolgu:
Proces ukonczony
Typ czolgu: A1M1 Abrams
Masa podwozia: 5000 [kg]
Typ podwozia: gasienicowy
Srednica dziala: 105 [mm]
Masa dziala: 10000 [kg]
Maksymalny promien razenia dziala: 4 [km]
Masa zbroi: 30000 [kg]
Wytrzymalosc zbroi: 3000 [punkty wytrzymalosci]
Masa silnika: 500 [kg]
Moc silnika: 1335 [KM]
Zuzycie paliwa: 37 [litr/100 km]
Czolg nie ma zamontowanego jeszcze zbiornika paliwa

Buduje zbiornik paliwa czolgu:
Proces ukonczony
Typ czolgu: A1M1 Abrams
Masa podwozia: 5000 [kg]
Typ podwozia: gasienicowy
Srednica dziala: 105 [mm]
Masa dziala: 10000 [kg]
Maksymalny promien razenia dziala: 4 [km]
Masa zbroi: 30000 [kg]
Wytrzymalosc zbroi: 3000 [punkty wytrzymalosci]
Masa silnika: 500 [kg]
Moc silnika: 1335 [KM]
Zuzycie paliwa: 37 [litr/100 km]
Maksymalna pojemnosc zbiornika paliwa: 1900
Poziom paliwa w zbiorniku paliwa: 1900


Czolg zlozony, ukonczony!!!

Biez czolg, znikaj i po polu fikaj!!!
Strony powiązane
strony powiązane
  1. sourcemaking.com/design_patterns/builder - strona z opisem wzorca design patterns [En]
  2. pl.wikipedia.org - opis wzorca na stronie Wikipedii

Komentarze