Giriş
Temelde iki çeşit Builder örüntüsü var
1. Blind Builder : Ayrı bir sınıftır
2. Bloch Builder : Kullanılmak istenen sınıfın içine public static bir builder yazılır.
3. Functional Builder : Sınıfa bir şeyler eklemek için array of functions kullanılır
Blind Builder ve Bloch Builder'ın Ortak ÖzellikleriHer iki Builder sınıfı da seçime bağlı özellikler için default değerler
sağlamalıdır. Zorunlu parametreler zaten kullanıcı tarafından verilecektir.
class Builder {
private String builderColor = "white"; // default color
private String builderWheels = "round"; // default wheels
private int builderSpeed = 100; // default speed
}
Build Edilen Sınıf Zorunlu Parametreleri final YapmalıdırBu alanlar artık
değişmeyecektir.
public class Car {
public static Builder builder() {
return new Builder();
}
// static inner class builder
public static class Builder {
...
}
// final (immutable) fields.
private final String color;
private final String wheels;
private final int speed;
// private constructor, only the builder calls this....
private Car(String color, String wheels, int speed) {
this.color = color;
this.wheels = wheels;
this.speed = speed;
}
}
1. Blind BuilderBuilder yarattığı nesneden
ayrıdır. Yaratılan nesne ise içine Builder alan private bir constructor veya setter metodları tanımlar.
Örnekclass CarBuilder {
private String wheels;
private String color;
private int speed;
...
}
Bu Builder tüm zorunlu ve isteğe bağlı parametreleri içinde biriktirir. Daha sonra build metodu ile yeni bir nesne yaratır. Dolayısıyla build metodu aşağıdakine benzer.
public Car build() {
// validate your attributes
Car car = new Car();
// set all attributes in your car
return car;
}
Eğer gerekirse build metodu içinde verinin doğruluğu da kontrol edilebilir.
public Car build(){
// validate your values e.g.
if (speed < 25) {
throw new IllegalValueException("Car is too slow.");
}
// further validation
Car car = new Car();
car.setWheels(wheels);
car.setColor(color);
car.setSpeed(speed);
return car;
}
2. Bloch BuilderBloch Builder, Effective Java kitabında gösteriliyor. Kullanılmak istenen sınıfın içine public static bir builder yerleştiriliyor. Yaratılan nesne ise içine Builder alan private bir constructor tanımlar.
Yukarıdaki örnekte Builder static ve nested bir sınıf. Kullanırken Pizza sınıfını yarattığı kolayca okunabilir.
Pizza pizza = new Pizza.Builder(12)
.cheese(true)
.pepperoni(true)
.bacon(true)
.build();
Örnek - Generics Kullanan Block Builder
Type parameters are not inherited from outer class to static nested class.
LanguageMatcher.Builder<MyClass, YourClass> lm =
new LanguageMatcher.Builder<MyClass, YourClass>();
Örnek
Ben şöyle yaptım
public class RemoteMapSourceParams<T, K, V> {
private final String mapName;
private final Predicate<K, V> predicate;
private final Projection<? super Map.Entry<K, V>, ? extends T> projection;
private RemoteMapSourceParams(Builder<T, K, V> builder) {
this.mapName = builder.mapName;
this.predicate = builder.predicate;
this.projection = builder.projection;
}
// Getters
public static <T, K, V> Builder<T, K, V> builder(String mapName) {
return new Builder<>(mapName);
}
public static class Builder<T, K, V> {
private final String mapName;
private String dataConnectionName;
private ClientConfig clientConfig;
private Predicate<K, V> predicate;
private Projection<? super Map.Entry<K, V>, ? extends T> projection;
public Builder(String mapName) {
Objects.requireNonNull(mapName, "mapName can not be null");
this.mapName = mapName;
}
//Setters
public RemoteMapSourceParams<T, K, V> build() {
return new RemoteMapSourceParams<>(this);
}
}
}
3. Functional BuilderÖrnek
Elimizde şöyle bir arayüz olsun
@FunctionalInterface
interface Coffee {
List<String> ingredients();
static Coffee withSaltedCaramelFudge(Coffee coffee) {
return () -> coffee.add("Salted Caramel Fudge");
}
default List<String> add(String item) {
return new ArrayList<>(ingredients()) {{
add(item);
}};
}
static Coffee withSweetenedMilk(Coffee coffee) {
return () -> coffee.add("Sweetened Milk");
}
static Coffee withDarkCookieCrumb(Coffee coffee) {
return () -> coffee.add("Dark Cookie Crumb");
}
static Coffee withVanillaAlmondExtract(Coffee coffee) {
return () -> coffee.add("Vanilla/Almond Extract");
}
}
@SafeVarargs
static Coffee getCoffeeWithExtra(Coffee coffee, Function<Coffee, Coffee>... ingredients) {
var reduced = Stream.of(ingredients)
.reduce(Function.identity(), Function::andThen);
return reduced.apply(coffee);
}
Bu kodu kullanmak için şöyle
yaparızrecord CoffeeCup(List<String> initialIngredient) implements Coffee {
@Override
public List<String> ingredients() {
return initialIngredient;
}
}
var ingredients = List.of("Tim Horton");
var coffeeCup = new CoffeeCup(ingredients);
var coffee = getCoffeeWithExtra(coffeeCup,
Coffee::withDarkCookieCrumb,
Coffee::withSaltedCaramelFudge,
Coffee::withSweetenedMilk,
Coffee::withVanillaAlmondExtract);
System.out.println("Coffee with " + String.join(", ", coffee.ingredients()));
}