Khi một Object có thể được tạo từ nhiều attributes khác nhau thì chúng ta nên consider sử dụng Builder Pattern thay cho Static factories hoặc Constructors.
Ví dụ sử dụng Builder Pattern:
// Builder Pattern public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate; public static class Builder { // Required parameters private final int servingSize; private final int servings; // Optional parameters - initialized to default values private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0; public Builder(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; } public Builder calories(int val) { calories = val; return this; } public Builder fat(int val) { fat = val; return this; } public Builder sodium(int val) { sodium = val; return this; } public Builder carbohydrate(int val) { carbohydrate = val; return this; } public NutritionFacts build() { return new NutritionFacts(this); } } private NutritionFacts(Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbohydrate = builder.carbohydrate; } }
Client tạo instance:
NutritionFacts cocaCola = new NutritionFacts .Builder(240, 8) .calories(100) .sodium(35) .carbohydrate(27).build();
Ưu điểm
- Code ở client dễ đọc.
- Có thể validate các parameters trước khi tạo instance bằng cách implement trong method build.
- Kỹ thuật kế thừa Abstract class với Builder
// Builder pattern for class hierarchies public abstract class Pizza { public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE } final Set<Topping> toppings; abstract static class Builder<T extends Builder<T>> { EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class); public T addTopping(Topping topping) { toppings.add(Objects.requireNonNull(topping)); return self(); } abstract Pizza build(); // Subclasses must override this method to return "this" protected abstract T self(); } } Pizza(Builder<?> builder) { toppings = builder.toppings.clone(); } }
- Mỗi parameter tương ứng với một method builder và Object được tạo khi invoke method build làm cho Builder Pattern flexible hơn so với các cách tạo Object khác.
Nhược điểm
- Chỉ hiệu quả đối với trường hợp class có nhiều optional attributes và việc sử dụng static factories hay constructor gây khó khăn cho client. Do vậy không nên quá lạm dụng Builder Pattern.
- Việc sử dụng Builder Pattern có thể gây ảnh hưởng performance vì mỗi lần tạo object sẽ phải tạo builder cho nó.
Tổng kết
In summary, the Builder pattern is a good choice when designing classes
whose constructors or static factories would have more than a handful of
parameters, especially if many of the parameters are optional or of identical
type. Client code is much easier to read and write with builders than with
telescoping constructors, and builders are much safer than JavaBeans.
Hy vọng bạn có được thêm kiến thức mới.
Keep learning 💪
Nhận xét
Đăng nhận xét