建造者模式——创建复杂对象的方法

  建造者模式属于创建型模式,目标是为了创建一个复杂的对象。具体的思路是:将一个复杂对象的创建过程和需要构建的部件或者数据进行分离,由一个建造者对象Builder来获取创建对象的部件或者数据,然后让一个指导者Director对象来指导创建过程,最终创建出需要的产品。
  构建者模式的使用场景大多是需要构建的对象依赖很多外部数据,尤其是使用同一类的数据的数量还不能确定的情况下,用单纯的bean对象来保存数据的方式就会显得很臃肿,在这种场景下,使用构建者模式就能很好的解决问题。比如我们在写sql语句的时候,select关键字和where关键字后面的条件大多数时候需要用户来指定,数量和逻辑是无法确定的,虽然查询的select语句的格式是固定的,但后面的参数不能确定,需要由调用方在使用的时候指定,在这种场景中,我们就可以将构建的框架select XXX from XXX where XXX的整体骨架和里面的参数XXX进行分离,在创建的过程中就会显得优雅很多,也具有良好的扩展性,后面我们会简单实现这个场景的示例代码。
  在java语言中,构建者模式往往和链式编程绑定在一起,所谓的链式编程,就是将原本一个bean的setter方法进行改造,让setter方法返回对象本身,这样在使用的时候就可以不用重新指定bean的名称,而是直接在一个setter方法后面接着写另一个setter方法。比如下面的代码,我们假定一个User类,需要设置它的属性,使用链式编程和不使用链式编程的方法如下:

  • 不使用链式编程的写法:
/**
 * 不适用链式编程的bean定义和使用(main方法中)
 */
public class User {
    private String name;
    private int age;
    private String gender;
    private String address;

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

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

    public void setGender(String gender) {
        this.gender = gender;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public static void main(String[] args) {
        User user = new User();
        user.setName("aaa");
        user.setAge(18);
        user.setGender("男");
        user.setAddress("中国");
    }
}
  • 使用链式编程的写法
/**
 * 适用链式编程的bean定义和使用(main方法中)
 */
public class User {
    private String name;

    private int age;

    private String gender;

    private String address;

    /**
     * setter方法不再是返回void,而是返回对象本身
     */
    public User setName(String name) {
        this.name = name;
        return this;
    }

    public User setAge(int age) {
        this.age = age;
        return this;
    }

    public User setGender(String gender) {
        this.gender = gender;
        return this;
    }

    public User setAddress(String address) {
        this.address = address;
        return this;
    }

    public static void main(String[] args) {
        User user = new User();
        user.setName("aaa")
            .setAge(18)
            .setGender("男")
            .setAddress("中国");
    }
}

建造者模式类图和说明

建造者模式类图

  • Builder :生成器的接口,定义创建对象的部件的方法
  • ConcreteBuilderA 和ConcreteBuilderB:构造器模式的具体实现类
  • Director :指导者,主要用来使用Builder接口获取到的部件或者数据,以一个统一的过程来构建所需的Product对象。

生成Select的sql场景示例

  我们需要构建一个select语句,我们都知道,查询的SQL语言的结构是select XXX from XXX where XXX,但是里面的参数是使用端来确定的,这个时候我们就可以使用构建者模式,将创建的结构和参数部件进行分离。从上面的结构图我们知道,构建者模式需要一个获取部件的Builder接口,和指导创建过程的Director两个角色,我们这里将这两个角色进行统一放到一个SqlBuilder中,其中的build()方法就完成了指导创建过程的职能,这样简化以后,我们在使用的时候写完参数,就直接可以使用build()方法创建出sql语句,而不需要再创建一个Director类了。具体代码如下:

1. 创建SqlBuilder接口

public interface SqlBuilder {

    /**
     * 添加select 后面的查询字段
     *
     * @param fieldName :
     * @return :
     */
    SqlBuilder addSelectField(String fieldName);

    /**
     * 表名称
     * @param tableName :
     * @return :
     */
    SqlBuilder tableName(String tableName);
    /**
     * where查询条件
     *
     * @return :
     */
    SqlBuilder where();

    /**
     * and条件
     *
     * @return :
     */
    SqlBuilder and();

    /**
     * or 条件
     *
     * @return :
     */
    SqlBuilder or();

    /**
     * 相等的条件过滤
     *
     * @param fieldName :
     * @param value :
     * @return :
     */
    SqlBuilder equalsFilter(String fieldName, String value);

    /**
     * like条件过滤
     *
     * @param fieldName :
     * @param value :
     * @return :
     */
    SqlBuilder likeFilter(String fieldName, String value);

    /**
     * in条件过滤
     *
     * @param fieldName :
     * @param values :
     * @return :
     */
    SqlBuilder inFilter(String fieldName, List<String> values);

    String build();
}

2. 写一个具体的实现类ConcreteSqlBuilder

  在实现类中,我们用两个StringBuilder来承接sql拼接的参数

/**
 * builder的具体实现
 */
public class ConcreteSqlBuilder implements SqlBuilder {

    private final StringBuilder selectFields = new StringBuilder();

    private String tableName;

    private final StringBuilder whereFilter = new StringBuilder();

    @Override
    public SqlBuilder addSelectField(String fieldName) {
        if (selectFields.length() == 0) {
            selectFields.append(" ").append(fieldName);
        } else {
            selectFields.append(" ,").append(fieldName);
        }
        return this;
    }

    @Override
    public SqlBuilder tableName(String tableName) {
        this.tableName = tableName;
        return this;
    }

    @Override
    public SqlBuilder where() {
        // where后面的单独放在一个StringBuilder中,不需要添加where字符串
        return this;
    }

    @Override
    public SqlBuilder and() {
        if (whereFilter.length() != 0) {
            whereFilter.append(" and ");
        }
        return this;
    }

    @Override
    public SqlBuilder or() {
        if (whereFilter.length() != 0) {
            whereFilter.append(" or ");
        }
        return this;
    }

    @Override
    public SqlBuilder equalsFilter(String fieldName, String value) {
        whereFilter.append(fieldName).append(" = '").append(value).append("'");
        return this;
    }

    @Override
    public SqlBuilder likeFilter(String fieldName, String value) {
        whereFilter.append(fieldName).append(" like '%").append(value).append("%'");
        return this;
    }

    @Override
    public SqlBuilder inFilter(String fieldName, List<String> values) {
        whereFilter.append(fieldName).append(" in (");
        for (int i = 0; i < values.size(); i++) {
            if (i != 0) {
                whereFilter.append(",");
            }
            whereFilter.append("'").append(values.get(i)).append("'");
        }

        whereFilter.append(")");
        return null;
    }

    @Override
    public String build() {
        //具体构建的过程
        StringBuilder sqlBuilder = new StringBuilder("select ");
        sqlBuilder.append(selectFields.toString()).append(" from ").append(tableName);
        if (whereFilter.length() == 0) {
            return sqlBuilder.toString();
        }
        sqlBuilder.append(" where ").append(whereFilter.toString());
        return sqlBuilder.toString();
    }
}

3. 写一个测试方法,展示其使用方法

public class Client {


    @Test
    public void testNoWhereSql(){
        SqlBuilder sqlBuilder = new ConcreteSqlBuilder();
        sqlBuilder.addSelectField("user_name")
            .addSelectField("sex")
            .addSelectField("address")
            .addSelectField("phone_number")
            .tableName("user");
        String sql = sqlBuilder.build();
        System.out.println("--------------no where sql -----------------");
        System.out.println(sql);
    }

    @Test
    public void testHasWhereSql(){
        SqlBuilder sqlBuilder = new ConcreteSqlBuilder();
        sqlBuilder.addSelectField("user_name")
            .addSelectField("sex")
            .addSelectField("address")
            .addSelectField("phone_number")
            .tableName("user")
            // where
            .where()
            .equalsFilter("sex","gender")
            .and()
            .likeFilter("address","aaa")
            .or()
            .inFilter("user_name", new ArrayList<>(Arrays.asList("aaa","bbbb","cccc")));
        String sql = sqlBuilder.build();
        System.out.println("--------------has where sql -----------------");
        System.out.println(sql);
    }
}

测试结果如下:

--------------no where sql -----------------
select  user_name ,sex ,address ,phone_number from user
--------------has where sql -----------------
select  user_name ,sex ,address ,phone_number from user where sex = 'gender' and address like '%aaa%' or user_name in ('aaa','bbbb','cccc')

从上面的代码示例中,我们可以看到,利用构建者模式和java的链式编程,将原本杂乱的sql拼接过程,以我们思维想对应的方式展现在我们的面前,在使用的时候,我们就可以用我们平常写sql的方式来拼接sql,最终得到我们想要的结果,整个拼接过程就会优雅很多。


后记
  个人总结,欢迎转载、评论、批评指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值