Mariusz Prowaźnik

o programowaniu w Javie, Scali i Clojure.


Wzorce projektowe - Builder

Builder jest wzorcem konstrukcyjnym - to znaczy, że jego zadaniem jest stworzenie i konfiguracja obiektu. Jego przydatność jest na tyle duża, że został mu poświęcony podrozdział książki "Effective Java 2nd edition".

Jak i kiedy zaimplementować ten wzorzec? Załóżmy, że napisaliśmy klasę o dużej ilości pól. Przykładowo:
public class Pojazd {
 private String marka;
 private Integer kola;
 private Integer poduszkiPowietrzne;
 private Integer predkoscMax;
 private String rejestracja;
//...
}
Konstruktor dla takiej klasy musiałby przyjmować dużo parametrów, w przypadku większych klas jego deklaracja byłaby bardzo długa i używanie go często skutkowałoby pomyłkami.
public Pojazd(String marka, String rejestracja, Integer kola,
   Integer poduszkiPowietrzne, Integer predkoscMax) {
  this.marka = marka;
  this.rejestracja = rejestracja;
  this.kola = kola;
  this.poduszkiPowietrzne = poduszkiPowietrzne;
  this.predkoscMax = predkoscMax;
 }
}
Pojazd pojazd = new Pojazd("marka", "rejestracja", 4, 2, 100);}
A co gdy nie wszystkie pola są wymagane? Trzeba by wtedy stworzyć różne konstruktory, albo przekazywać nulle jako parametry.
public Pojazd(String marka, String rejestracja, Integer kola,
   Integer poduszkiPowietrzne, Integer predkoscMax) {
  // ...
 }

 public Pojazd(String marka, String rejestracja, Integer kola,
   Integer poduszkiPowietrzne) {
  // ...
 }

 public Pojazd(String marka, String rejestracja, Integer kola) {
  // ...
 }

 public Pojazd(String marka, String rejestracja) {
  // ...
 }

Właśnie w takich sytuacjach warto użyć wzorca Builder. Do klasy Pojazd dodajemy wewnętrzną klasę statyczną Builder, zawierającą metody o nazwach odpowiadających nazwom pól klasy Pojazd, oraz metodę build(), która będzie tworzyć obiekt klasy Pojazd. Ten sam kod będzie wyglądać w ten sposób:
public class Pojazd {
 private String marka;
 private String rejestracja;
 private Integer kola;
 private Integer poduszkiPowietrzne;
 private Integer predkoscMax;

 public static class Builder {
  // parametr obowiązkowy
  private final Integer kola;

  // parametry opcjonalne
  private String marka = null;
  private String rejestracja = null;
  private Integer poduszkiPowietrzne = null;
  private Integer predkoscMax = null;

  public Builder(Integer kola) {
   this.kola = kola;
  }

  public Builder marka(String marka) {
   this.marka = marka;
   return this;
  }

  public Builder rejestracja(String rejestracja) {
   this.rejestracja = rejestracja;
   return this;
  }

  public Builder poduszkiPowietrzne(Integer poduszkiPowietrzne) {
   this.poduszkiPowietrzne = poduszkiPowietrzne;
   return this;
  }

  public Builder predkoscMax(Integer predkoscMax) {
   this.predkoscMax = predkoscMax;
   return this;
  }

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

 private Pojazd(Builder builder) {
  this.marka = builder.marka;
  this.rejestracja = builder.rejestracja;
  this.kola = builder.kola;
  this.poduszkiPowietrzne = builder.poduszkiPowietrzne;
  this.predkoscMax = builder.predkoscMax;
 }
}
Stworzenie obiektu:
Pojazd pojazd = new Pojazd.Builder(4).marka("marka")
    .poduszkiPowietrzne(4).predkoscMax(300)
    .rejestracja("rejestracja").build();
Parametry obowiązkowe są przekazywane jako parametry konstruktora, a parametry opcjonalne jako parametry jednoargumentowych metod. Co ważne, metody te zwracają this, przez co możliwe jest używanie ich "w ciągu". Wywołanie metody build tworzy obiekt klasy Pojazd z ustawionymi polami.

Brak komentarzy :

Prześlij komentarz