设计模式学习(四):建造者模式


设计模式学习(四):建造者模式

作者:Grey

原文地址:

CSDN:设计模式学习(四):建造者模式

建造者模式

建造者模式是创建型模式。

我们在对一个实体类进行属性的 get 或 set 的时候,可以通过封装一些常用的构造方法来简化实体类的构造。

比如 Effective Java 中举到到这个例子

// Effective Java 3th examples
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;
    }
}

其中 Builder 就是一个内部类,用于构造 NutritionFacts 的必要信息,外部调用 NutritionFacts 的构造方法时候,可以这样使用:

NutritionFacts cocaCola = new NutritionFacts.Builder(240,8).calories(100).sodium(35).carbohydrate(27).build();

image

构造器模式也适用于类层次结构。抽象类有抽象的 Builder ,具体类有具体的 Builder 。Effective Java中还有一个例子, 假设我们抽象出一个披萨类,各种各样的披萨均可以继承披萨这个抽象类来实现自己的具体类型的披萨。

Pizza抽象类如下:

import java.util.*;

// Effective Java 3th examples
public abstract class Pizza {
    public enum Topping {HAM, MUSHROOM, ONION, PEPPER, SAUSAGE}
    final Set toppings;
  
    abstract static class Builder> {
        EnumSet 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(); // See Item 50
    }
}

其中的 Builder 方法是抽象的,所以子类需要实现具体的 Builder 策略,

一种披萨的具体实现 NyPizza

import java.util.Objects;

public class NyPizza extends Pizza {
    public enum Size {SMALL, MEDIUM, LARGE}

    private final Size size;

    public static class Builder extends Pizza.Builder {
        private final Size size;

        public Builder(Size size) {
            this.size = Objects.requireNonNull(size);
        }

        @Override
        public NyPizza build() {
            return new NyPizza(this);
        }

        @Override
        protected Builder self() {
            return this;
        }
    }

    private NyPizza(Builder builder) {
        super(builder);
        size = builder.size;
    }
}

另一种披萨的具体实现 Calzone

public class Calzone extends Pizza {
    private final boolean sauceInside;

    public static class Builder extends Pizza.Builder {
        private boolean sauceInside = false; // Default

        public Builder sauceInside() {
            sauceInside = true;
            return this;
        }

        @Override
        public Calzone build() {
            return new Calzone(this);
        }

        @Override
        protected Builder self() {
            return this;
        }
    }

    private Calzone(Builder builder) {
        super(builder);
        sauceInside = builder.sauceInside;
    }
}

我们在具体调用的时候,可以通过如下方式:

NyPizza pizza = new NyPizza.Builder(SMALL).addTopping(SAUSAGE).addTopping(ONION).build();
Calzone calzone = new Calzone.Builder().addTopping(HAM).sauceInside().build();

实际应用有非常多,很多组件都提供这样的构造方式,比如 OkHttp Client 的构造方法:

public static OkHttpClient create(long connectTimeOut) {
        return new OkHttpClient().newBuilder().connectionSpecs(Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT)).connectTimeout(connectTimeOut, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS).retryOnConnectionFailure(true).followRedirects(true).followSslRedirects(true).hostnameVerifier((s, sslSession) -> true).cookieJar(new CookieJar() {
            private List cookies;

            @Override
            public void saveFromResponse(HttpUrl url, List cookies) {
                this.cookies = cookies;
            }

            @Override
            public List loadForRequest(HttpUrl url) {
                if (cookies != null) {
                    return cookies;
                }
                return Collections.emptyList();
            }
        }).build();
    }

建造者模式的主要应用

  • JDK 中的 Calender
Calendar calendar = new Calendar.Builder().build();
  • MyBatis 中 CacheBuilder.build()SqlSessionFactoryBuilder.build()

  • Spring 中 BeanDefinitionBuilder.getBeanDefinition()方法

UML 和 代码

UML 图

代码

更多

设计模式学习专栏

参考资料

  • Effective Java中文版(第3版)

  • Head First 设计模式

  • 设计模式-可复用面向对象软件的基础

  • 坦克大战-马士兵

  • 菜鸟教程-设计模式

  • 极客时间-设计模式之美-王争