wzorzec budowniczy:
Wzorzec budowniczy należy do grupy wzorców kreacyjnych. Daje on możliwość tworzenia złożonych obiektów etapami. Pozwala na stworzenie różnych typów oraz reprezentacji obiektu używając do tego samego kodu konstrukcyjnego.
Zastosowanie wzorca:
- kiedy chcemy pozbyć się wieloopcjonalnego konstruktora
- kiedy chcemy tworzyć różne reprezentacje danego obiektu
- kiedy chcemy skonstruować drzewo kompozytowe lub inne złożone obiekty
Aby lepiej zrozumieć działanie tego wzorca rozważmy następującą sytuację. Wyobraź sobie, że chcemy stworzyć obiekt typu komputer. Wszyscy wiemy, że komputery składają się z wielu podzespołów. Poza tym możemy wybierać między komputerami stacjonarnymi, a laptopami. W moim przykładzie napiszemy program który wyświetli nam opis komputera i jego podzespołów. Będziemy mogli stworzyć cztery rodzaje komputerów: zwykły stacjonarny, laptop, stacjonarny gamingowy oraz laptop gamingowy. Aby tego dokonać będziemy potrzebowali:
- komponentów z jakich ma powstać produkt lub etapów budowy
- produktu – komputer
- kierownika budowy
- interfejsu budowniczego
- konkretnych budowniczych
- klienta
Przyjrzyjmy się każdemu z nich:
- Komponenty – będą to klasy takie jak dysk twardy czy karta graficzna gdzie umieścimy odpowiednie metody, aby móc pobrać i ustawić niezbędne informacje. Możemy uznać, że są to nasze etapy budowy komputera.
package components;
public class HardDrive {
private final double quantity;
private final String producent;
public HardDrive(double quantity, String producent) {
this.quantity = quantity;
this.producent = producent;
}
public double getQuantity(){
return quantity;
}
public String getProducent(){
return producent;
}
}
package components;
public class GraphicsCard {
private final String name;
public GraphicsCard(String name) {
this.name = name;
}
public String getName(){
return name;
}
}
- Produkt – to powstały obiekt. Dla nas będzie to finalnie komputer. Należy zwrócić uwagę, że produkty konstruowane przez różnych budowniczych nie muszą należeć do tej samej hierarchii klas czy interfejsu.
package computers;
import components.*;
import components.System;
public class Computer {
private final ComputerType computerType;
private final Cooling cooling;
private final HDMI hdmi;
private final SoundBoard soundBoard;
private final NetworkInterfaceCard networkInterfaceCard;
private final Case aCase;
private final Mainboard mainboard;
private final RAM ram;
private final System system;
private final USB usb;
private final PowerSupply powerSupply;
private final CPU CPU;
private final GraphicsCard graphicsCard;
private final HardDrive hardDrive;
private final CardReader cardReader;
private final ExtraCooling extraCooling;
public Computer(ComputerType computerType, Cooling cooling,
HDMI hdmi, SoundBoard soundBoard, NetworkInterfaceCard networkInterfaceCard,
Case aCase, Mainboard mainboard, RAM ram, System system, USB usb, PowerSupply powerSupply,
CPU CPU, GraphicsCard graphicsCard, HardDrive hardDrive,
CardReader cardReader, ExtraCooling extraCooling){
this.computerType = computerType;
this.CPU = CPU;
this.graphicsCard = graphicsCard;
this.hardDrive = hardDrive;
this.cooling = cooling;
this.hdmi=hdmi;
this.soundBoard = soundBoard;
this.networkInterfaceCard = networkInterfaceCard;
this.aCase = aCase;
this.mainboard = mainboard;
this.ram=ram;
this.system=system;
this.usb=usb;
this.powerSupply = powerSupply;
this.cardReader = cardReader;
this.extraCooling = extraCooling;
}
public String getInfo(){
String info="";
info+="Typ komputera: "+ computerType +"\n";
info+="Procesor: "+ CPU.getProcesor()+"\n";
info+="Plyta glowna: "+ mainboard.getName()+", producent: "+ mainboard.getProducent()+"\n";
info+="Ram: "+ram.getQuantity()+" GB, producent: "+ram.getProducent()+"\n";
info+="Dysk: Producent: "+ hardDrive.getProducent()+", pojemnosc: "+ hardDrive.getQuantity()+"GB"+"\n";
info+="Karta graficzna: "+ graphicsCard.getName()+"\n";
info+="Karta dzwiekowa: "+ soundBoard.getName()+"\n";
info+="Karta sieciowa: "+ networkInterfaceCard.getName()+"\n";
info+="System operacyjny: "+system.getSystem()+"\n";
info+="Ilosc portow USB: "+usb.getQuantity()+"\n";
info+="Ilosc wejsc HDMI: "+hdmi.getQuantity()+"\n";
if(this.cardReader != null) {
info += "Cztnik kart: Producent: " + cardReader.getProducent() + ", ilosc: " + cardReader.getQuantity() + "\n";
}
info+="Chlodzenie: "+ cooling.getProducent()+"\n";
if(this.extraCooling !=null) {
info += "Dodatkowe chlodzenie: " + extraCooling.getName() + "\n";
}
info+="Zasilanie: producent: "+ powerSupply.getProducent()+", standard: "+ powerSupply.getStandard()+", moc: "+ powerSupply.getPower()+"W\n";
info+="Obudowa: "+ aCase.getName()+", producent: "+ aCase.getProducent()+"\n";
return info;
}
}
- Kierownik budowy – to on będzie definiował kolejność wykonywania etapów konstrukcyjnych, aby móc stworzyć i następnie użyć ponownie określonych konfiguracji produktów. Nasz kierownik definiuje cztery metody konstrukcyjne: dla zwykłego komputera stacjonarnego, dla zwykłego laptopa, dla stacjonarnego komputera gamingowego oraz laptopa gamingowego.
package director;
import builders.ComputerBuilder;
import components.*;
import components.System;
import computers.ComputerType;
public class Director {
public void constructNormalComputer(ComputerBuilder builder){
builder.setComputerType(ComputerType.STACJONARNY_ZWYKLY);
builder.setCPU(new CPU("Intel"));
builder.setGraphicsCard(new GraphicsCard("Radeon"));
builder.setHardDrive(new HardDrive(1000,"Samsung"));
builder.setCooling(new Cooling("Chlodzenie podstawowe"));
builder.setHDMI(new HDMI(1));
builder.setSoundBoard(new SoundBoard("Zintegorwana karta dzwiekowa"));
builder.setNetworkInterfaceCard(new NetworkInterfaceCard("Podstawowa Karta Sieciowa"));
builder.setCase(new Case("Zwykła czarna obudowa", "Dom"));
builder.setMainboard(new Mainboard("Zwykla plyta glowna", "Dom"));
builder.setRAM(new RAM(4,"Dom"));
builder.setSystem(new System("Windows xp"));
builder.setUSB(new USB(2));
builder.setPowerSupply(new PowerSupply(550,"AMV", "Dom"));
}
public void constructNormalLaptop(ComputerBuilder builder){
builder.setComputerType(ComputerType.LAPTOP_ZWYKLY);
builder.setCPU(new CPU("Intel core i5"));
builder.setGraphicsCard(new GraphicsCard("Radeon"));
builder.setHardDrive(new HardDrive(1000,"Samsung"));
builder.setCooling(new Cooling("Chlodzenie podstawowe do laptopa"));
builder.setHDMI(new HDMI(1));
builder.setSoundBoard(new SoundBoard("Zintegorwana karta dzwiekowa do laptopa"));
builder.setNetworkInterfaceCard(new NetworkInterfaceCard("Podstawowa Karta Sieciowa do laptopa"));
builder.setCase(new Case("Zwykła czarna obudowa", "Dom"));
builder.setMainboard(new Mainboard("Zwykla plyta glowna do laptopa", "Dom"));
builder.setRAM(new RAM(4,"Dom"));
builder.setSystem(new System("Windows 7"));
builder.setUSB(new USB(3));
builder.setPowerSupply(new PowerSupply(650,"AMV", "Dom"));
}
public void constructNormalComputerGame(ComputerBuilder builder){
builder.setComputerType(ComputerType.STACJONARNY_DO_GIER);
builder.setCPU(new CPU("Intel core i7"));
builder.setGraphicsCard(new GraphicsCard("Radeon x2"));
builder.setHardDrive(new HardDrive(3000,"Toshiba"));
builder.setCooling(new Cooling("Chlodzenie do komputera do gier"));
builder.setExtraCooling(new ExtraCooling("Dodatkowe chlodzenie"));
builder.setCardReader(new CardReader(2, "GaMe"));
builder.setHDMI(new HDMI(2));
builder.setSoundBoard(new SoundBoard("Karta graficzna do gier"));
builder.setNetworkInterfaceCard(new NetworkInterfaceCard("Karta sieciowa"));
builder.setCase(new Case("Podświatelana obudowa", "GaMe"));
builder.setMainboard(new Mainboard("Rozbudowana plyta glowna", "GaMe"));
builder.setRAM(new RAM(16,"GaMe"));
builder.setSystem(new System("Windows 10 Professional"));
builder.setUSB(new USB(6));
builder.setPowerSupply(new PowerSupply(650,"AMV", "GaMe"));
}
public void constructLaptopGame(ComputerBuilder builder){
builder.setComputerType(ComputerType.LAPTOP_DO_GIER);
builder.setCPU(new CPU("Intel core i9"));
builder.setGraphicsCard(new GraphicsCard("Radeon x2"));
builder.setHardDrive(new HardDrive(3000,"Toshiba SSD"));
builder.setCooling(new Cooling("Chlodzenie do komputera do gier"));
builder.setExtraCooling(new ExtraCooling("Dodatkowe chlodzenie podświatlane"));
builder.setCardReader(new CardReader(2, "GaMe"));
builder.setHDMI(new HDMI(2));
builder.setSoundBoard(new SoundBoard("Karta graficzna do gier"));
builder.setNetworkInterfaceCard(new NetworkInterfaceCard("Karta sieciowa"));
builder.setCase(new Case("Podświatelana obudowa", "GaMe"));
builder.setMainboard(new Mainboard("Rozbudowana plyta glowna do laptopa", "GaMe"));
builder.setRAM(new RAM(16,"GaMe"));
builder.setSystem(new System("Windows 10 Professional"));
builder.setUSB(new USB(4));
builder.setPowerSupply(new PowerSupply(650,"AMV", "GaMe"));
}
}
- Interfejs budowniczego – to on deklaruje etapy konstrukcji obiektu wspólnych dla wszystkich budowniczych. W naszym przypadku zadeklarowałam ustawienie odpowiednich podzespołów komputera.
package builders;
import components.*;
import components.System;
import computers.ComputerType;
public interface Builder {
void setCooling(Cooling cooling);
void setHardDrive(HardDrive dysk);
void setHDMI(HDMI hdmi);
void setSoundBoard(SoundBoard soundBoard);
void setGraphicsCard(GraphicsCard graphicsCard);
void setNetworkInterfaceCard(NetworkInterfaceCard networkInterfaceCard);
void setCase(Case aCase);
void setMainboard(Mainboard mainboard);
void setCPU(CPU CPU);
void setRAM(RAM ram);
void setSystem(System system);
void setUSB(USB usb);
void setPowerSupply(PowerSupply powerSupply);
void setComputerType(ComputerType computerType);
}
- Konkretni budowniczowie – to oni zapewniają różne implementacje etapów konstrukcji. W naszym przypadku będzie to budowniczy który stworzy ogólnie komputer. (Tu mogliśmy stworzyć dwóch budowniczych: do komputera gamingowego jak i zwykłego, ale nie jest to konieczne)
package builders;
import components.*;
import components.System;
import computers.Computer;
import computers.ComputerType;
public class ComputerBuilder implements Builder {
private ComputerType computerType;
private Cooling cooling;
private HDMI hdmi;
private SoundBoard soundBoard;
private NetworkInterfaceCard networkInterfaceCard;
private Case aCase;
private Mainboard mainboard;
private RAM ram;
private System system;
private USB usb;
private PowerSupply powerSupply;
private CPU CPU;
private GraphicsCard graphicsCard;
private HardDrive drive;
private CardReader cardReader;
private ExtraCooling extraCooling;
@Override
public void setComputerType(ComputerType computerType) { this.computerType = computerType; }
@Override
public void setCPU(CPU CPU) { this.CPU = CPU; }
@Override
public void setRAM(RAM ram) {this.ram=ram; }
@Override
public void setSystem(System system) { this.system=system; }
@Override
public void setUSB(USB usb) { this.usb=usb; }
@Override
public void setPowerSupply(PowerSupply powerSupply) { this.powerSupply = powerSupply; }
@Override
public void setGraphicsCard(GraphicsCard graphicsCard) { this.graphicsCard = graphicsCard; }
@Override
public void setNetworkInterfaceCard(NetworkInterfaceCard networkInterfaceCard) {
this.networkInterfaceCard = networkInterfaceCard;
}
@Override
public void setCase(Case aCase) { this.aCase = aCase; }
@Override
public void setMainboard(Mainboard mainboard) { this.mainboard = mainboard; }
@Override
public void setCooling(Cooling cooling) { this.cooling = cooling; }
public void setCardReader(CardReader cardReader) {
this.cardReader = cardReader;
}
public void setExtraCooling(ExtraCooling extraCooling) {
this.extraCooling = extraCooling;
}
@Override
public void setHardDrive(HardDrive drive) { this.drive =drive; }
@Override
public void setHDMI(HDMI hdmi) {this.hdmi=hdmi; }
@Override
public void setSoundBoard(SoundBoard soundBoard) {
this.soundBoard = soundBoard;
}
public Computer getResult(){
return new Computer(computerType, cooling,hdmi, soundBoard,
networkInterfaceCard, aCase, mainboard, ram, system, usb, powerSupply, CPU,
graphicsCard, drive, cardReader, extraCooling);
}
}
Zanim zobaczymy klasę kliencką pokażemy uproszczony diagram klas:

Klasa ComputerMain wygląda następująco:
import builders.ComputerBuilder;
import director.Director;
import computers.Computer;
public class ComputerMain {
public static void main(String[] args){
Director director = new Director();
ComputerBuilder builder = new ComputerBuilder();
director.constructLaptopGame(builder);
Computer computer = builder.getResult();
System.out.println(computer.getInfo());
}
}
Widzimy, że na początku tworzymy obiekt kierownika, a następnie odpowiedniego budowniczego. Nasz kierownik wywołuje odpowiednia metodę konstrukcyjna na naszym budowniczym. Finalny produkt jakim jest w naszym przypadku laptop gamingowy jest pobierany od budowniczego, ponieważ kierownik nic o nim nie wie. Rezultat wywołania:
Typ komputera: LAPTOP_DO_GIER
Pocesor: Intel core i9
Plyta glowna: Rozbudowana plytaglowna do laptopa, producent: GaMe
Ram: 16.0 GB, producent GaMe
Dysk: Producent: Toshiba SSD, pojemnosc: 3000.0 GB
Karta graficzna: Radeon x2
Karta dzwiekowa: Kartadzwiekowa do gier
Karta sieciowa: Karta sieciowa
System operacyjny: Windows 10 Professional
Ilosc portow USB: 4
Ilosc wejsc HDMI: 2
Czytnik kart: Producent: GaMe, ilosc: 2
Chlodzenie: Chlodzenie do komputera do gier
Dodatkowe chlodzenie: Dodatkowe chlodzenie podswietlane
Zasilanie: producent: GaMe, standdard AMV,moc 650W
Obudowa: Podswietlana obudowa, producent: GaMe
Zobaczmy to samo, ale bez użycia wzorca.
import component.*;
import component.OS;
import computers.Computer;
public class ComputerMain {
public static void main(String[] args) {
Cooling cooling = new Cooling("Chlodzenie do laptopa gamingowego");
HardDrive hardDrive = new HardDrive(2000, "Toshiba");
HDMI hdmi = new HDMI(1);
SoundBoard soundBoard = new SoundBoard("Karta dzwiekowa do laptopa gamingowego");
GraphicsCard graphicsCard = new GraphicsCard("Karta graficzna do laptopa gamingowego");
NetworkInterfaceCard networkInterfaceCard = new NetworkInterfaceCard("Karta sieciowa do laptopa gamingowego");
Case aCase = new Case("Obudowa", "LaPtOp GaMe");
Mainboard mainboard = new Mainboard("Plyta glowna", "LaPtOp GaMe");
CPU cpu = new CPU("Intel i7");
RAM ram = new RAM(8, "LaPtOp GaMe");
OS operatingSystem = new OS("Windows 10");
USB usb = new USB(4);
PowerSupply powerSupply = new PowerSupply(550, "AVM", "LaPtOp GaMe");
CardReader cardReader = new CardReader(1, "LaPtOp GaMe");
ExtraCooling extraCooling = new ExtraCooling("Dodatkowe chlodzenie do laptopa");
Computer laptopGame = new Computer(
KomputerType.LAPTOP_DO_GIER,
cooling, hardDrive, hdmi,
soundBoard, graphicsCard, networkInterfaceCard,
aCase, mainboard, cpu, ram, operatingSystem,
usb, powerSupply, cardReader, extraCooling);
System.out.println(laptopGame.getInfo());
}
}
Aby stworzyć nowy obiekt komputer musimy zainicjować stworzenie każdego komponentu. Następnie tworzymy komputer więc musimy w jego konstruktorze umieścić wszystkie komponenty. Następnie pobieramy informacje o nim. Rezultat wykonania jest identyczny. Musimy na dodatek pamiętać, że nie każdy komputer ma np dodatkowe chłodzenie, a zatem w konstruktorze mogą pojawić się wartości null. W metodzie getInfo musimy zatem obsłużyć odpowiednie wyjątki.
Klasa computer:
package computers;
import component.*;
public class Computer implements ComputerInterface {
private KomputerType komputerType;
private Cooling cooling;
private HardDrive dysk;
private HDMI hdmi;
private SoundBoard soundBoard;
private GraphicsCard graphicsCard;
private NetworkInterfaceCard networkInterfaceCard;
private Case aCase;
private Mainboard mainboard;
private CPU CPU;
private RAM ram;
private OS OS;
private USB usb;
private PowerSupply powerSupply;
private CardReader cardReader;
private ExtraCooling extraCooling;
public Computer(KomputerType komputerType, Cooling cooling, HardDrive dysk, HDMI hdmi,
SoundBoard soundBoard, GraphicsCard graphicsCard, NetworkInterfaceCard networkInterfaceCard,
Case aCase, Mainboard mainboard, CPU CPU, RAM ram,
OS OS, USB usb, PowerSupply powerSupply, CardReader cardReader,
ExtraCooling extraCooling) {
this.komputerType = komputerType;
this.cooling = cooling;
this.dysk=dysk;
this.hdmi=hdmi;
this.soundBoard = soundBoard;
this.graphicsCard = graphicsCard;
this.networkInterfaceCard = networkInterfaceCard;
this.aCase = aCase;
this.mainboard = mainboard;
this.CPU = CPU;
this.ram=ram;
this.OS = OS;
this.usb=usb;
this.powerSupply = powerSupply;
this.cardReader = cardReader;
this.extraCooling = extraCooling;
}
@Override
public String getInfo() {
String info="";
try {
info += "Typ: " + komputerType + "\n";
}catch (NullPointerException exp){info+="Typ: Brak \n";}
try {
info += "Chlodzenie: " + cooling.getProducent() + "\n";
}catch (NullPointerException exp){info+="Chlodzenie: Brak \n";}
try {
info += "Dysk: Producent: " + dysk.getProducent() + ", pojemnosc: " + dysk.getPojemnosc() + "GB" + "\n";
}catch(NullPointerException exp){info+="Dysk: Brak \n";}
try {
info += "Ilosc wejsc HDMI: " + hdmi.getIloscHdmi() + "\n";
}catch(NullPointerException exp){info+="HDMI: Brak \n";}
try {
info += "Karta dzwiekowa: " + soundBoard.getDzwiek() + "\n";
}catch(NullPointerException exp){info+="Karta dzwiekowa: Brak \n";}
try {
info += "Karta graficzna: " + graphicsCard.getGrafika() + "\n";
}catch(NullPointerException exp){info+="Karta graficzna: Brak \n";}
try {
info += "Karta sieciowa: " + networkInterfaceCard.getSieciowa() + "\n";
}catch(NullPointerException exp){info+="Karta sieciowa: Brak \n";}
try {
info += "Obudowa: " + aCase.getNazwa() + ", producent: " + aCase.getProducent() + "\n";
}catch(NullPointerException exp){info+="Obudowa: Brak \n";}
try {
info += "Plyta glowna: " + mainboard.getNazwa() + ", producent: " + mainboard.getProducent() + "\n";
}catch(NullPointerException exp){info+="Płyta główna: Brak \n";}
try {
info += "Procesor: " + CPU.getProcesor() + "\n";
}catch(NullPointerException exp){info+="Procesor: Brak \n";}
try {
info += "RAM: " + ram.getIlosc() + " GB, producent: " + ram.getProducent() + "\n";
}catch(NullPointerException exp){info+="RAM: Brak \n";}
try {
info += "System operacyjny: " + OS.getSystem() + "\n";
}catch(NullPointerException exp){info+="System operacyjny: Brak \n";}
try {
info += "Ilosc portow USB: " + usb.getIlosc() + "\n";
}catch(NullPointerException exp){info+="USB: Brak \n";}
try {
info += "Zasilanie: producent: " + powerSupply.getProducent() + ", standard: " + powerSupply.getStandard() + ", moc: " +
powerSupply.getMoc() + "W\n";
}catch(NullPointerException exp){info+="Zasilanie: Brak \n";}
try {
info += "Dodatkowe chlodzenie: " + extraCooling.getDodatkoweChlodzenie() + "\n";
}catch(NullPointerException exp){info+="Dodatkowe chłodzenie: Brak \n";}
try{
info += "Cztnik kart: Producent: " + cardReader.getProducent() + ", ilosc: " + cardReader.getIloscCzytnikow() + "\n";
}catch (NullPointerException exp){info+="Czytnik kart: Brak \n";}
return info;
}
}
Klasy komponentów pominę – każdy z nich ma konstruktor oraz odpowiednie gettery. Widzimy, że bez użycia wzorca budowniczy niezbędne operacje do stworzenia obiektu nie są hermetyzowane i na dodatek nie jest ukryta wewnętrzna reprezentacja produktu.