设计模式系列篇(三)——建造者模式

What

为了灵活构造复杂对象,该对象会有多个成员变量,在外部调用的时候,不需要或者不方便一次性创建出所有的成员变量,在这种情况下,使用多个构造方法去构建对象,很难维护,这时候Builder设计模式解决这个问题,进行buid()方法中创建对象,并且将builder传入,该builder中,维护了传入对象的成员变量。

  • 与工厂模式的区别:
    工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。建造者模式是用来创建一种类型的复杂对象,可以通过设置不同的可选参数,“定制化”地创建不同的对象。

Why

如果一个类中有很多属性,为了避免构造函数的参数列表过长,影响代码的可读性和易用性,我们可以通过构造函数配合 set() 方法来解决。但是,如果存在下面情况中的任意一种,我们就要考虑使用建造者模式了。

  1. 我们把类的必填属性放到构造函数中,强制创建对象的时候就设置。如果必填的属性有很多,把这些必填属性都放到构造函数中设置,那构造函数就又会出现参数列表很长的问题。如果我们把必填属性通过 set() 方法设置,那校验这些必填属性是否已经填写的逻辑就无处安放了。
  2. 如果类的属性之间有一定的依赖关系或者约束条件,我们继续使用构造函数配合 set() 方法的设计思路,那这些依赖关系或约束条件的校验逻辑就无处安放了。
  3. 如果我们希望创建不可变对象,也就是说,对象在创建好之后,就不能再修改内部的属性值,要实现这个功能,我们就不能在类中暴露 set() 方法。构造函数配合 set() 方法来设置属性值的方式就不适用了。

How

这里,以电商平台里常见的搜索商品的搜索条件,来向大家介绍建造者模式的经典实现。需求是这样的:

  1. 用户名username是必传项,如果 username为空,直接抛出异常;
  2. useCategorycategory为可选项,如果useCategorytrue,代表使用类别信息去筛选,加快搜索效率。此时,category不能为空,否则抛异常;
  3. fuzzy代表是否开启模糊查询,如果为true时,搜素关键字keywordcategory(如果不为空的情况下)前后添加%,方便构造SQL语句;

建造者模式实现起来很简单,直接上代码:

public class QueryCondition {
    private String username;

    private Boolean useCategory;
    private String category;

    private Boolean fuzzy;
    private String keyword;

    private Integer pageSize;
    private Integer pageNo;

    private QueryCondition(QueryConditionBuilder builder) {
        this.username = builder.username;
        this.useCategory = builder.useCategory;
        this.category = builder.category;
        this.fuzzy = builder.fuzzy;
        this.keyword = builder.keyword;
        this.pageSize = builder.pageSize;
        this.pageNo = builder.pageNo;
    }


    @Override
    public String toString() {
        return String.format("%s is querying products, the query condition is [useCategory = %s," +
                "category = %s, " +
                "fuzzy = %s, " +
                "keyword = '%s', " +
                "pageSize = %d, " +
                "pageNo = %d]", username, useCategory, category, fuzzy, keyword, pageSize, pageNo);
    }

    public static class QueryConditionBuilder {
        private String username;

        private Boolean useCategory;
        private String category;

        private Boolean fuzzy;
        private String keyword;

        private Integer pageSize;
        private Integer pageNo;

        public QueryConditionBuilder username(String username) {
            this.username = username;
            return this;
        }

        public QueryConditionBuilder useCategory(Boolean useCategory) {
            this.useCategory = useCategory;
            return this;
        }

        public QueryConditionBuilder category(String category) {
            this.category = category;
            return this;
        }

        public QueryConditionBuilder fuzzy(Boolean fuzzy) {
            this.fuzzy = fuzzy;
            return this;
        }

        public QueryConditionBuilder keyword(String keyword) {
            this.keyword = keyword;
            return this;
        }

        public QueryConditionBuilder pageSize(Integer pageSize) {
            this.pageSize = pageSize;
            return this;
        }

        public QueryConditionBuilder pageNo(Integer pageNo) {
            this.pageNo = pageNo;
            return this;
        }

        public QueryCondition build() {
            if (this.username == null || this.username.isEmpty()) {
                throw new IllegalArgumentException("username can not be empty");
            }

            if (this.useCategory) {
                if (this.category == null || this.category.isEmpty()) {
                    throw new IllegalArgumentException("category can not be empty when useCategory is true");
                }
            }

            if (this.fuzzy) {
                if (this.keyword != null && !this.keyword.isEmpty()) {
                    this.keyword = "% " + this.keyword + " %";
                }
                if (this.category != null && !this.category.isEmpty()) {
                    this.category = "%" + this.category + "%";
                }
            }

            return new QueryCondition(this);
        }
    }

}

代码地址

i-learning

写在最后

如果你觉得我写的文章帮到了你,欢迎点赞、评论、分享、赞赏哦,你们的鼓励是我不断创作的动力~

你可能感兴趣的:(设计模式系列篇(三)——建造者模式)