Wzorzec projektowy Builder- opis i przykład zastosowania

Problem do rozwiązania:

  1. Potrzebujemy elastyczności w tworzeniu nowych obiektów, inicjując tylko niektóre pola klasy.
  2. Chcemy zapewnić swobodę przy tworzeniu obiektów, ale po ich powstaniu nie dopuszczamy zmian.
  3. Chcemy uniknąć tworzenia dużej liczby konstruktorów z wieloma polami, które trzeba przekazać do utworzenia obiektu.

lub

  1. Nie chcemy dawać elastyczności przy tworzeniu nowych obiektów przez użytkownika.
  2. Definicje, jak skonstruować dany obiekt będą przypisane w innych klasach.

Opis wzorca Builder:

Wzorzec ten możemy zaimplementować na dwa sposoby:

  1. Poprzez klasę wewnętrzną;
  2. Poprzez interfejs

Schemat UML rozwiązania z klasą wewnętrzną:

Schemat działania:

W rozwiązaniu z klasą wewnętrzną, w klasie House występują tylko prywatne pola i ich gettery (nie definiujemy setterów pól klasy „budowanej”).

  1. Za wytworzenie obiektów klasy House odpowiada inna klasa, tj. wewnętrzna klasa HouseBuiler.
  2. Klasa HouseBuilder ma zdublowane prywatne pola z klasy budowanej i publiczne metody typu HouseBuilder, ustawiające zdublowane pola wewnątrz swojego ciała na wartości przekazane poprzez metody „budujące”.
  3. Wywołując poszczególne metody budujące HouseBuildera np. buildWalls(String walls), inicjalizujemy obiekt i przypisujemy przekazane poprzez metody wartości do pól prywatnych klasy HouseBuiler.
  4. Co istotne, na końcu każdej takiej metody zwracamy obiekt this (po kawałku tworzymy obiekt, inicjalizując poszczególne, wybrane pola metodami) .
  5. W finalnej metodzie build() ostatecznie zwracamy cały nowo zdefiniowany obiekt typu House, przekazując poprzez konstruktor this ostatecznie stworzony obiekt HouseBuildera.
  6. W konstruktorze klasy House następuje przypisanie pól z HouseBuildera do prywatnych pól klasy House.

Fragment klasy main:

        House house = new House.HouseBuilder()
                .buildWalls("walls")
                .buildFloors("floors")
                .buildRoof("roof")
                .buildRooms("rooms")
                .build();

Schemat UML rozwiązania z interfejsem:

Schemat działania:

W rozwiązaniu z Interfejsem, w klasie House występują  prywatne pola, ich gettery oraz settery.

  1. W interfejsie HouseBuilder deklarujemy metody budujące, odpowiadające budowaniu pól klasy House oraz metodę typu House getHouse().
  2. Za wytworzenie obiektów klasy House odpowiadają inne klasy tj. dyrektor oraz tzw. buildery, w których definiujemy metody stanowiące, jak będzie wyglądał nasz dom (implementujące interfejs HouseBuilder).
  3. Builder jak np. SmallHouseBuilder zawiera prywatne pole typu House i nadpisanie metod z interfejsu jako settery z klasy House dla prywatnego, własnego pola typu House („budowanie” obiektu następuje według metod z tej klasy, według określonego przez nas „przepisu” po wywołaniu odpowiednich metod).
  4. Całością procesu budowy zarządza dyrektor, tj. klasa HouseDirector. Zawiera ona prywatne pole typu HouseBuilder, konstruktor, metodę budującą buildHouse (to w jej definicji są wywołane metody HouseBuildera, niekoniecznie wszystkie i w takiej kolejności, jak w klasie buildera, generalnie jak wymyśli to sobie dyrektor, bo to on zarządza budową) i metodę typu House getHouse().
  5. Na początku tworzymy obiekty klas implementujących interfejs HouseBuilder  np. smallHauseBuilder.
  6. Następnie, tworząc obiekt dyrektora, przekazujemy mu w konstruktorze utworzony obiekt.
  7. Po utworzeniu obiektu dyrektora, możemy na nim wywołać metodę buildHouse(), a wiec wybudować obiekt według wytycznych dyrektora.
  8. Jeśli chcemy zobaczyć nasz wybudowany obiekt, to wywołujemy metodę getHouse() klasy HouseDirectior (która to z kolei zwraca metodę getHouse() konkretnego buildera).

Fragment klasy main:

        SmallHouseBuilder smallHouseBuilder = new SmallHouseBuilder();
        HouseDirectior smallHouseDirector = new HouseDirectior(smallHouseBuilder);
        smallHouseDirector.buildHouse();
        House smallHouse = smallHouseDirector.getHouse();

Kod programu przed zastosowaniem wzorca:

Klasa main:

import House.House;

public class Main {
    public static void main(String[] args) {

        House house1 = new House("walls", "floors", "rooms", "roof", "windows", "doors");
        House house2 = new House("walls", "floors", "roof");


        System.out.println(house1);
        System.out.println(house2);
    }
}

Klasa House:

package House;

public class House {

    private String walls;
    private String floors;
    private String rooms;
    private String roof;
    private String windows;
    private String doors;
    private String garage;


    public House(String walls, String floors, String rooms, String roof, String windows, String doors, String garage) {
        this.walls = walls;
        this.floors = floors;
        this.rooms = rooms;
        this.roof = roof;
        this.windows = windows;
        this.doors = doors;
        this.garage = garage;
    }

    public House(String walls, String floors, String rooms, String roof, String windows, String doors) {
        this.walls = walls;
        this.floors = floors;
        this.rooms = rooms;
        this.roof = roof;
        this.windows = windows;
        this.doors = doors;
    }

    public House(String walls, String floors, String roof) {
        this.walls = walls;
        this.floors = floors;
        this.roof = roof;
    }


    public String getWalls() {
        return walls;
    }

    public void setWalls(String walls) {
        this.walls = walls;
    }

    public String getFloors() {
        return floors;
    }

    public void setFloors(String floors) {
        this.floors = floors;
    }

    public String getRooms() {
        return rooms;
    }

    public void setRooms(String rooms) {
        this.rooms = rooms;
    }

    public String getRoof() {
        return roof;
    }

    public void setRoof(String roof) {
        this.roof = roof;
    }

    public String getWindows() {
        return windows;
    }

    public void setWindows(String windows) {
        this.windows = windows;
    }

    public String getDoors() {
        return doors;
    }

    public void setDoors(String doors) {
        this.doors = doors;
    }

    public String getGarage() {
        return garage;
    }

    public void setGarage(String garage) {
        this.garage = garage;
    }

    @Override
    public String toString() {
        return "House{" +
                "walls='" + walls + '\'' +
                ", floors='" + floors + '\'' +
                ", rooms='" + rooms + '\'' +
                ", roof='" + roof + '\'' +
                ", windows='" + windows + '\'' +
                ", doors='" + doors + '\'' +
                ", garage='" + garage + '\'' +
                '}';
    }
}

Kod programu po zastosowaniu wzorca z klasą wewnętrzną:

Klasa main:

import House.House;

public class Main {
    public static void main(String[] args) {

        House house = new House.HouseBuilder()
                .buildWalls("walls")
                .buildFloors("floors")
                .buildRoof("roof")
                .buildRooms("rooms")
                .build();

        System.out.println(house);
    }
}

Klasa House:

package House;

public class House {

    private String walls;
    private String floors;
    private String rooms;
    private String roof;
    private String windows;
    private String doors;
    private String garage;

    private House(HouseBuilder houseBuilder){
        this.walls = houseBuilder.walls;
        this.floors = houseBuilder.floors;
        this.rooms = houseBuilder.rooms;
        this.roof = houseBuilder.roof;
        this.windows = houseBuilder.windows;
        this.doors = houseBuilder.doors;
        this.garage = houseBuilder.garage;
    }

    public String getWalls() {
        return walls;
    }
    public String getFloors() {
        return floors;
    }
    public String getRooms() {
        return rooms;
    }
    public String getRoof() {
        return roof;
    }
    public String getWindows() {
        return windows;
    }
    public String getDoors() {
        return doors;
    }
    public String getGarage() {
        return garage;
    }

    @Override
    public String toString() {
        return "House{" +
                "walls='" + walls + '\'' +
                ", floors='" + floors + '\'' +
                ", rooms='" + rooms + '\'' +
                ", roof='" + roof + '\'' +
                ", windows='" + windows + '\'' +
                ", doors='" + doors + '\'' +
                ", garage='" + garage + '\'' +
                '}';
    }

    public static class HouseBuilder {

        private String walls;
        private String floors;
        private String rooms;
        private String roof;
        private String windows;
        private String doors;
        private String garage;

        public HouseBuilder buildWalls(String walls){
            this.walls = walls;
            return this;
        }

        public HouseBuilder buildFloors(String floors){
            this.floors = floors;
            return this;
        }

        public HouseBuilder buildRooms(String rooms){
            this.rooms = rooms;
            return this;
        }

        public HouseBuilder buildRoof(String roof){
            this.roof = roof;
            return this;
        }

        public HouseBuilder buildWindows(String windows){
            this.windows = windows;
            return this;
        }

        public HouseBuilder buildDoors(String doors){
            this.doors = doors;
            return this;
        }

        public HouseBuilder buildGarage(String garage){
            this.garage = garage;
            return this;
        }

        public House build(){
            return new House(this);
        }
    }
}

Kod programu po zastosowaniu wzorca z interfejsem:

Klasa main:

import house.BigHouseBuilder;
import house.House;
import house.HouseDirectior;
import house.SmallHouseBuilder;

public class Main {
    public static void main(String[] args) {

        SmallHouseBuilder smallHouseBuilder = new SmallHouseBuilder();
        HouseDirectior smallHouseDirector = new HouseDirectior(smallHouseBuilder);
        smallHouseDirector.buildHouse();
        House smallHouse = smallHouseDirector.getHouse();

        BigHouseBuilder bigHouseBuilder = new BigHouseBuilder();
        HouseDirectior bigHouseDirector = new HouseDirectior(bigHouseBuilder);
        bigHouseDirector.buildHouse();
        House bigHouse = bigHouseDirector.getHouse();

        System.out.println(smallHouse);
        System.out.println(bigHouse);

    }
}

Klasa House:

package house;

public class House {

    private String walls;
    private String floors;
    private String rooms;
    private String roof;
    private String windows;
    private String doors;
    private String garage;


    public String getWalls() {
        return walls;
    }

    public void setWalls(String walls) {
        this.walls = walls;
    }

    public String getFloors() {
        return floors;
    }

    public void setFloors(String floors) {
        this.floors = floors;
    }

    public String getRooms() {
        return rooms;
    }

    public void setRooms(String rooms) {
        this.rooms = rooms;
    }

    public String getRoof() {
        return roof;
    }

    public void setRoof(String roof) {
        this.roof = roof;
    }

    public String getWindows() {
        return windows;
    }

    public void setWindows(String windows) {
        this.windows = windows;
    }

    public String getDoors() {
        return doors;
    }

    public void setDoors(String doors) {
        this.doors = doors;
    }

    public String getGarage() {
        return garage;
    }

    public void setGarage(String garage) {
        this.garage = garage;
    }

    @Override
    public String toString() {
        return "House{" +
                "walls='" + walls + '\'' +
                ", floors='" + floors + '\'' +
                ", rooms='" + rooms + '\'' +
                ", roof='" + roof + '\'' +
                ", windows='" + windows + '\'' +
                ", doors='" + doors + '\'' +
                ", garage='" + garage + '\'' +
                '}';
    }
}

Klasa HouseDirector:

package house;

public class HouseDirectior {

    private HouseBuilder houseBuilder;

    public HouseDirectior(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }

    public void buildHouse(){
        houseBuilder.buildWalls();
        houseBuilder.buildFloors();
        houseBuilder.buildRooms();
        houseBuilder.buildRoof();
        houseBuilder.buildWindows();
        houseBuilder.buildDoors();
        houseBuilder.buildGarage();
    }

    public House getHouse(){
        return this.houseBuilder.getHouse();
    }
}

Interfejs HouseBuilder:

package house;

public interface HouseBuilder {

    void buildWalls();
    void buildFloors();
    void buildRooms();
    void buildRoof();
    void buildWindows();
    void buildDoors();
    void buildGarage();

    House getHouse();
}

Klasa SmallHouseBuilder:

package house;

public class SmallHouseBuilder implements HouseBuilder {

    private House house;

    public SmallHouseBuilder() {
        this.house = new House();
    }

    @Override
    public void buildWalls() {
        this.house.setWalls("small walls");
    }

    @Override
    public void buildFloors() {
        this.house.setFloors("small floors");
    }

    @Override
    public void buildRooms() {
        this.house.setRooms("small rooms");
    }

    @Override
    public void buildRoof() {
        this.house.setRoof("small roof");
    }

    @Override
    public void buildWindows() {
        this.house.setWindows("small windows");
    }

    @Override
    public void buildDoors() {
        this.house.setDoors("small doors");
    }

    @Override
    public void buildGarage() {
        this.house.setGarage("small garage");
    }

    @Override
    public House getHouse() {
        return house;
    }
}

Klasa BigHouseBuilder:

package house;

public class BigHouseBuilder implements HouseBuilder{

    private House house;

    public BigHouseBuilder() {
        this.house = new House();
    }

    @Override
    public void buildWalls() {
        this.house.setWalls("big walls");
    }

    @Override
    public void buildFloors() {
        this.house.setFloors("big floors");
    }

    @Override
    public void buildRooms() {
        this.house.setRooms("big rooms");
    }

    @Override
    public void buildRoof() {
        this.house.setRoof("big roof");
    }

    @Override
    public void buildWindows() {
        this.house.setWindows("big windows");
    }

    @Override
    public void buildDoors() {
        this.house.setDoors("big doors");
    }

    @Override
    public void buildGarage() {
        this.house.setGarage("big garage");
    }

    @Override
    public House getHouse() {
        return house;
    }
}


Dodaj komentarz

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