设计模式-建造者模式

前言

在日常开发中,我们经常见到建造者模式的身影,我们看下建造者模式的定义

The intent of the Builder design pattern is to separate the construction of a complex object from its representation. By doing so the same construction process can create different representations.
将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示

场景

当一个类需要很多种不同的构造函数,且在不同场景有不同的参数可选需求,这时候可以使用建造者模式来简化使用方式。

问题所在:

如下是一个User类:

public class User {
    private String name;
    private String sex;
    private Integer age;
    private String language;
    private String area;
}

通常我们会使用两种方式来使用这个类

一个是构造方法:

    public User(String name) {
        this.name = name;
    }

    public User(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

    public User(String name, String sex, Integer age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

若是当前类有非常多的属性,那么我们在复杂业务中可能创建大量的构造方法,这样在使用的时候,我们并不清楚哪种构造方法在哪种场景更合适使用;时间长了会阅读困难,且容易造成误解;

一个是set方法

	public void setName(String name) {
        this.name = name;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setLanguage(String language) {
        this.language = language;
    }

    public void setArea(String area) {
        this.area = area;
    }

这种方式在构建过程中对象的状态容易发生变化,造成错误。因为类的属性是分步设置的;

建造者模式实现

public class User {
    private String name;
    private String sex;
    private Integer age;
    private String language;
    private String area;

    private User(Builder builder) {
        this.name = builder.name;
        this.sex = builder.sex;
        this.age = builder.age;
        this.language = builder.language;
        this.area = builder.area;
    }

    public static class Builder {
        private String name;
        private String sex;
        private Integer age;
        private String language;
        private String area;

        public Builder setName(String name) {
            this.name = name;
            return this;
        }

        public Builder setSex(String sex) {
            this.sex = sex;
            return this;
        }

        public Builder setAge(Integer age) {
            this.age = age;
            return this;
        }

        public Builder setLanguage(String language) {
            this.language = language;
            return this;
        }

        public Builder setArea(String area) {
            this.area = area;
            return this;
        }
        public User build() {
            return new User(this);
        }
    }
}

乍一看这样写法觉得很复杂,但是当你使用起来你会发现:

        User user = new Builder()
                .setName("小明")
                .setAge(12)
                .setSex("男")
                .build();

和lombok的@Builder一样;

场景

有人说了,我这样操作一波有什么意义呢?我直接一个一个set也是完全Ok的。

那我们随意找一个在开源代码中的使用,在平常我们会经常使用线程池,我们便说一下线程池中的建造者模式的使用。

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {}

我们熟知线程池有七个参数,且每个参数的意思也都很清楚;

        return new ThreadPoolExecutor(4, 4, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
		// TODO
                return null;
            }
        }, new ThreadPoolExecutor.AbortPolicy());

假设我们new了一个这样的线程池,那么如果实现ThreadFactory接口就比较麻烦,因为我们需要new一个完整的线程对象;那么怎么简化这样的操作呢?
在google的guava包中便有这样一个类ThreadFactoryBuilder,让我们来看看他帮我们做了什么?

看一下这个类的组成,可以清楚看见这是一个建造者模式的完整实现,我们可以使用builder来快速的构建一个线程对象;

new ThreadFactoryBuilder().setNameFormat("test-thread-%d").build()

这样对比,是否简单明了很多?

posted @ 2021-03-30 20:42  faylinn  阅读(72)  评论(0编辑  收藏  举报
、、、