美文网首页JavaJava 杂谈Java服务器端编程
复杂对象的构建——建造者模式

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

作者: RunAlgorithm | 来源:发表于2019-05-13 23:04 被阅读0次

1. 定义

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

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

对比现实世界,类似于:

  • 要造一台计算机主机,需要CPU、内存卡、显卡、电源等。组装过程也相对复杂,比如一些接线的处理。
  • 要造一辆汽车,需要底盘、方向盘、发动机、座椅、轮子等等。
  • 要造航空母舰,需要甲板、起飞装置、机库、动力系统、武器库等。

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

  • 整体由多个部件组成。
  • 丰富的组装配置。
  • 复杂的组装流程。

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

2. 设计

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

2.1. 有抽象建造者

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

主要角色有:

  • 抽象建造者(Abstract Builder),声明需要的部件。
  • 具体建造者(Concrete Builder),定义明确的部件。
  • 指挥者(Director),指挥组装过程。
  • 产品(Product),产品,由多个部件组成。

类图如下:

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

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

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,声明产品需要的部件,并且把部件组合成产品
  • 产品 Product,目标产品,由多个部件组成。

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

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

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

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

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

3. 应用

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

有抽象建造者的:

  • 产品由多个部件组成,不同部件可以更换,不同部件的配置和更换为新的产品。

    比如电脑,同一个品牌的电脑,不同的 CPU 配置和内存配置,可以视为一个新的产品。

  • 部件之间存在复杂的依赖关系。

  • 创建过程中还依赖到外部环境的对象。

    一般由指挥者去依赖外部环境,然后把相关的对象交给建造者去处理。

无抽象建造者的:

  • 产品由多个部件组成
  • 部件之间的依赖关系简单
  • 创建过程中不需要依赖外部环境

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

  • 复杂对象的构建和实现分离,比如客户端的对话框的构建。
  • 多参数方法的改良,如果一个对象的创建需要多个可选参数,而且参数还会变化和扩展,应用建造者模式可以让代码简洁,易与扩展与编写。

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. 多参数方法改良举例

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

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

  • 必选项
    • 下载的 url 地址
    • 下载后的文件的保存地址
  • 可选项
    • 是否支持多线程,默认不支持
    • 是否支持文件覆盖,默认可以
    • 下载失败重试次数,默认10次

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

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. 和抽象工厂模式的区别

  • 建造者模式返回一个完整产品,抽象工厂返回一套产品
  • 建造者模式侧重构造和组建复杂产品,工厂模式侧重生产不同类型的产品。

相关文章

  • 程序员内功心法【设计模式】之建造者模式

    基本定义 建造者模式将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。 建造者模式构建复杂对象...

  • java设计模式--建造者模式

    java设计模式--建造者模式 建造者模式定义 建造者模式:是将复杂对象的构建与表示进行分离,使同样的构建过程会有...

  • 设计模式-建造者模式

    建造者模式 建造者模式介绍 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 建造者模式...

  • 建造者模式

    建造者模式 概述 建造者模式(Builder pattern),将一个复杂对象的构建与它的表示分离,使得同样的构建...

  • 设计模式(4) 建造者模式

    什么是建造者模式 经典建造者模式的优缺点 对建造者模式的扩展 什么是建造者模式 建造者模式将一个复杂的对象的构建与...

  • 设计模式 | 建造者模式及典型应用

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

  • 建造者模式(2)-原理类图

    建造者模式介绍 建造者模式(Builder Pattern)又叫生成器模式,是一种对象构建模式。它可以将复杂对象的...

  • 设计模式之建造者模式

    什么是建造者模式 建造者模式又叫生成器模式, 是一种对象构建模式 可以将复杂对象的构建过程抽象出来, 使这个抽象过...

  • Android常见设计模式—Builder模式

    Builder(建造者模式) 建造者模式:是将一个复杂的对象的构建与它的表示分离(同构建不同表示),使得同样的构建...

  • Java设计模式之建造者模式(原型/链式)

    什么是建造者模式? 建造者模式是一种创建型模式,建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可...

网友评论

    本文标题:复杂对象的构建——建造者模式

    本文链接:https://www.haomeiwen.com/subject/rybdaqtx.html