JavaJava 杂谈Java服务器端编程

复杂对象的构建——建造者模式

2019-05-13  本文已影响0人  RunAlgorithm

1. 定义

建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。

可以用来简化复杂对象的构造过程。

对比现实世界,类似于:

复杂对象一般具有这些特点:

建造者模式,把复杂的对象创建过程进行剥离,尤其是内部部件的配置,进行独立变化和维护。

2. 设计

建造者模式,一般有两种实现。

2.1. 有抽象建造者

有抽象建造者会比较复杂。对建造者进一步抽象,意味着可以有不同的建造者实现。

主要角色有:

类图如下:

建造者模式-有抽象建造者-类图

产品类,由多个部分组成,不同部分的组合可以视为一个新的产品:

public class Product {

    private String part1;
    private String part2;
    private String part3;

    public void setPart1(String part1) {
        this.part1 = part1;
    }

    public void setPart2(String part2) {
        this.part2 = part2;
    }

    public void setPart3(String part3) {
        this.part3 = part3;
    }

    @Override
    public String toString() {
        return String.format("产品:[part1=%s, part2=%s, part3=%s]", part1, part2, part3);
    }
}

抽象构建者:

public interface IBuilder {
     void build1();
     void build2();
     void build3();
     Product getResult();
}

具体构建者 A:

public class BuilderA implements IBuilder {

    private Product product;

    public BuilderA() {
        product = new Product();
    }

    public void build1() {
        product.setPart1("A1");
    }

    public void build2() {
        product.setPart2("A2");
    }

    public void build3() {
        product.setPart3("A3");
    }

    public Product getResult() {
        return product;
    }
}

指挥者,指挥构建者生成产品:

public class Director {
    private IBuilder builder;
    public Director(IBuilder builder) {
        this.builder = builder;
    }

    public Product construct() {
        builder.build1();
        builder.build2();
        builder.build3();
        return builder.getResult();
    }
}

调用的地方如下:

public class TestBuilder {

    public static void main(String[] args) {

        // 生成 A 产品
        IBuilder builder = new BuilderA();
        Director director = new Director(builder);
        Product product = director.construct();
        System.out.println(product.toString());

        // 生成 B 产品
        builder = new BuilderB();
        director = new Director(builder);
        product = director.construct();
        System.out.println(product.toString());
    }
}

2.2. 无抽象建造者

无抽象建造者的建造者模式,其实是有抽象建造者的一个退化。

比如只生产一种产品,就没有必要对建造者进行抽象。

无抽象建造者模式很简单,基本角色有:

对比有抽象建造者的建造者模式,这里的 Builder 其实承担了指挥者的角色。

建造者模式-无抽象建造者-类图

这个模式,可以对产品的部件进行灵活的配置。而且可以使用点语法和链式编程,代码结构清晰。

具体怎样的配置交给外部的使用者进行选择。

大量的开源框架使用 Builder 来进行配置。

3. 应用

建造者模式适用于复杂对象的构造,两种不同建造者有自己的适用范围。

有抽象建造者的:

无抽象建造者的:

在这些应用场景会非常有效:

3.1. 复杂对象构建举例

一个对话框由多个部件组成,比如标题栏、内容、确定按钮、取消按钮等等。有的页面需要标题栏,有的页面不需要,有的页面需要确定按钮,有的不需要。

这些都通过 Builder 来构建这个复杂的对话框。

CommonAlertDialogFragment builder = new CommonAlertDialogFragment.Builder(getActivity())
    .setMessage(xxxx)
    .setCancelable(true)
    .setNegativeButtonText(xxx, null)
    .setPositiveButtonText(xxx, null)
    .setOnDismissListener(new OnDismissListener() {
        @Override
        public void onDismiss() {
            ...
        }
    });
CommonAlertDialogFragment dialog = builder.create()

3.2. 多参数方法改良举例

在方法参数类型较多的时候,我们会使用方法多态,但这样会造成构造方法数量膨胀。

假设我们现在要设计一个下载器,有这么一些配置:

如果直接用方法多态的方式,如果想要尽可能地覆盖这些条件的组合所有的情况,需要很多的重载方法,于是有这样的代码:

void downloadFile(String url, String savePath);
void downloadFile(String url, String savePath, boolean multiThread);
void downlaodFile(String url, String savePath, boolean multiThread, boolean overwrite);
void downloadFile(String url, String savePath, boolean multiThread, boolean overwrite, int retryNum);
void downloadFile(String url, String savePath, int retryNum);
...

后续增减参数类型,都会修改到这些构造方法,不符合开闭原则。

所以,我们用建造者模式来改善它:

public static class Builder {
    private String url;
    private String savePath;
    private int retryNum = 0;
    private boolean multiThread = false;
    private boolean overwrite = true;

    public Builder(String url, String savePath) {
        this.url = url;
        this.savePath = savePath;
    }

    public Builder addRetryNum(int retryNum) {
        this.retryNum = retryNum;
        return this;
    }

    public Builder addMultiThread(boolean multiThread) {
        this.multiThread = multiThread;
        return this;
    }

    public Builder addOverwrite(boolean overwrite) {
        this.overwrite = overwrite;
        return this;
    }

    public DownloadParams build() {
        return new DownloadParams(url, savePath, retryNum, callBack, multiThread, overwrite, sync);
    }
}

然后方法缩减为只有一个:

void download(DownloadParams params);

使用的地方按需配置。

3.3. JDK:Locale

Locale 有一个通过建造者创建的方式

 Locale aLocale = new Builder()
    .setLanguage("sr")
    .setScript("Latn")
    .setRegion("RS")
    .build();

3.4. JDK:StringBuilder

String 是不可变字符串,每次操作都会创建新的字符串。

StringBuilder 对其进行了优化,可以通过 append 操作进行字符串拼接,支持非 String 的基本数据类型。

String str = (new StringBuilder())
    .append(1)
    .append(1.2f)
    .append("Hello World!")
    .toString();

3.5. Gson:GsonBuilder

Gson 是 Google 提供的 Gson 解析框架。

Gson 可以通过 GsonBuilder 注册一些自定义的解析器,用来对 Bean 的反射做定制化处理。

GsonBuilder buider = new GsonBuilder().
    .registerTypeAdapter(MediaBean.class, new MediaBeanDeserializer())
    .registerTypeAdapter(UserBean.class, new UserBeanDeserializer())
    .registerTypeAdapter(LiveBean.class, new LiveBeanDeserializer());
Gson gson = builder.create();

3.6. OkHttp:OkHttpClient.Builder

OkHttp 是 Square 公司开源的 Http 客户端,用来进行网络请求,已经支持到 Http/2。

使用这个客户端,需要配置超时时间,是否重定处理,是否允许连接失败重试等。

这里就是使用建造者模式来完成 OkHttp 的配置和实例化的:

OkHttpClient.Builder builder = new OkHttpClient.Builder()
    .connectTimeout(20, TimeUnit.MILLISECONDS)
    .readTimeout(60, TimeUnit.MILLISECONDS)
    .writeTimeout(60, TimeUnit.MILLISECONDS)
    .followRedirects(true);
    .followSslRedirects(true);
    .retryOnConnectionFailure(true);
OkHttpClient okHttpClient = builder.build();

4. 特点

4.1. 优势

4.2. 缺点

4.3. 注意事项

4.4. 和抽象工厂模式的区别

上一篇 下一篇

猜你喜欢

热点阅读