设计模式第一章(建造者模式)

发布于:2025-09-10 ⋅ 阅读:(17) ⋅ 点赞:(0)

设计模式第一章(建造者模式)

建造者模式(Builder Pattern)是一种创建型设计模式,将复杂对象的构建过程与其表示分离,使同一构建流程能生成不同形态的对象。 ‌例如:工厂 单列 原型 目的都是造对象。

前言

核心思想

​ 通过拆分构建步骤与对象结构,将复杂对象的创建分为多个独立模块。客户端通过指挥者类间接构建对象,无需直接调用具体构建逻辑,从而隐藏实现细节并提升代码可维护性。 ‌

角色分工

  1. ‌**建造者(Builder)**‌:定义构建复杂对象的接口,包含多个抽象方法对应不同构建阶段。

  2. ‌**具体建造者(ConcreteBuilder)**‌:实现建造者接口,负责按步骤组装对象部件。

    ‌**指挥者(Director)**‌:控制构建流程,通过调用建造者方法生成最终对象,不直接暴露构建细节。 ‌

优势

  • 分离构建与表示‌:降低代码耦合度,同一构建逻辑可适配不同产品形态。 ‌
  • 逐步构建‌:通过分步组装简化复杂对象创建流程。 ‌
  • 封装性‌:客户端仅需指定对象类型和参数,无需了解内部构建细节。 ‌
  • 灵活性‌:修改构建逻辑时无需改动客户端代码,便于维护和扩展。 ‌

代码部分

  • 第一版本 ~ 最终的版本
  • 每个版本的需求变化已经不足之处
  • 如果优化每个版本的缺陷
  • 最终写一个应用的场景
  • 核心思想-链式调用

V1

  • 需求背景
  1. 我们现在有一个普通的javabean对象,需求为,如果年龄大小于10并且名字为 tom那么我们就提示不能创建;
  2. 如果年龄大于10,并且名字叫 Jerry 的话,也不能创建
java 代码部分
public class UserV1 {


    private String name;

    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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


    public boolean check() {
        if (age < 10 && "Tom".equals(name)) {
            return false;
        }
        if (age > 10 && "Jerry".equals(name)) {
            return false;
        }
        return true;
    }


    public static void main(String[] args) {
        // 10 岁不能叫 tom
        // 大于 10岁  jetty 也不行

        UserV1 userV1 = new UserV1();
        userV1.setAge(1);
        userV1.setName("Tom");
        //传统判断  调用时机不对,不调用都有可能
        // 有没有一种方案是 必须是设置了属性后再校验, 因为属性可能需要上下文
        if (userV1.check()) {

        }
    }
缺陷部分

我们看到上面的代码缺陷部分,调用时机不对,如果调用者不使用,那么就不能校验了,而且这个校验需要有上下文的时机,必须是属性都设置了后再调用


V2

我们针对上面的缺点进行改进,使用一个内部的builder 对象构建

代码部分
public class User {

    private String name;

    private int age;

    private User() {

    }


    public static Builder builder() {
        return new Builder();
    }

    //创建一个内部类
    public static class Builder {
        private String name;

        private int age;

        private Builder() {

        }

        public User build() throws IllegalAccessException {


            User user = new User();
            user.name = this.name;
            user.age = this.age;


            if (age < 10 && "Tom".equals(name)) {
                throw new IllegalAccessException("年龄小于10不能叫Tom");
            }
            if (age > 10 && "Jerry".equals(name)) {
                throw new IllegalAccessException("年龄大于10不能叫Jerry");
            }
            return user;
        }

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

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

}
使用部分

我们通过User.builder()方法得到一个用户对象,在最红调用build的时候,我们进行校验,这样就保证了上下文的顺序

public class TestV2Main {

    public static void main(String[] args) throws IllegalAccessException {
        // 缺陷   user创建之后还可以修改里面的属性,实际上是一次性的对象
        User user =  User.builder().name("Tom").age(11).build();
        user.setAge(222);

    }
}
缺陷部分

当我们build完后这个对象,我们还是可以通过set更改里面的属性,实际上这个对象是一次性的,当我们设置后,就不能够进行更改。类似于我们发起的 http对象

V3

对外提供的builde方法,在创建之后就不让修改

代码部分
public class User {

    private final String name;

    private final int age;

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


    public static Builder builder() {
        return new Builder();
    }

    //创建一个内部类
    public static class Builder {
        private String name;

        private int age;

        private Builder() {

        }

        public User build() throws IllegalAccessException {
            // 可以判断   必须设置值
            User user = new User(this);

            if (age < 10 && "Tom".equals(name)) {
                throw new IllegalAccessException("年龄小于10不能叫Tom");
            }
            if (age > 10 && "Jerry".equals(name)) {
                throw new IllegalAccessException("年龄大于10不能叫Jerry");
            }
            return user;
        }

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

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

    public String getName() {
        return name;
    }


    public int getAge() {
        return age;
    }


}
使用部分
    public static void main(String[] args) throws IllegalAccessException {

        // dsl
        User user = User.builder().name("Tom").age(11).build();
        
          user.setAge()//会报错,没有该方法

            // 案例演示
        List.of(1,2,3,4).stream().map(String::valueOf).filter(x -> x.length() > 1).collect(Collectors.toList());

    }
总结

该部分代码的演变为,增加一个私有构造方法,使之外面不能直接new,然后去掉set方法,只保留get方法,在 builder 里面进行复制,内部类的使用方式

使用案例

我们根据传入的参数,构建一个 sql 的查询和修改的类,使之做到 dsl

v1

我们定义了枚举,SELECT,DELETE,UPDATE 采用 Switch 实现,记住一点,如果我们一开始想不出来使用设计模式,那么我们就按照最基本的方式,先把功能实现

代码部分
public class SqlWarp {

    private SqlWarp () {

    }

    public static SqlBuilder builder(SqlType sqlType) {
        return new SqlBuilder(sqlType);
    }

    public static class SqlBuilder {
         final SqlType sqlType;

         // 查询的列
         private String[] columns;

         //查询的表名
         private String tableNm;


         // 查询的where 条件
         private String where;

        private Map<String,String> setMap = new LinkedHashMap<>();

        private SqlBuilder(SqlType sqlType) {
            this.sqlType = sqlType;
        }

        public SqlBuilder select(String ...columns) {
            this.columns = columns;
            return this;
        }

        public SqlBuilder table(String tableNm) {
            this.tableNm = tableNm;
            return this;
        }

        public SqlBuilder where(String where) {
            this.where = where;
            return this;
        }

        public SqlBuilder set(String column,String columnValue) {
            setMap.put(column,columnValue);
            return this;
        }

        public String buildSql() {
            StringBuilder sql = new StringBuilder();
            switch (sqlType) {
                case SELECT -> {
                    sql.append(" SELECT ").append(String.join(",",columns)).append(" FROM ").append(tableNm);
                    if (where != null) {
                        sql.append(" WHERE ").append(where);
                    }
                }
                case UPDATE -> {
                    sql.append(" UPDATE ").append(tableNm).append(" SET ");
                    sql.append(setMap.entrySet().stream()
                            .map(entry -> entry.getKey() + "=" + entry.getValue())
                            .collect(Collectors.joining(",")));
                    if (where != null) {
                        sql.append(" WHERE ").append(where);
                    }
                }
                case INSERT -> {

                }
                case DELETE -> {

                }
                default -> throw new UnsupportedOperationException("暂不支持该类型的参数");
            }

            return sql.toString();
        }
    }


    enum SqlType {
        SELECT,
        UPDATE,
        DELETE,
        INSERT,
    }

}
使用部分
public class SqlTestMain {

    public static void main(String[] args) {
        // 问题点,如果我是 select 我还是可以调用set方法,那么有没有一种功能,限制调用的api
        String sql = SqlWarp.builder(SqlWarp.SqlType.SELECT)
                .select("name", "age", "address")
                .table("user")
                .where(" age = 1")
                .buildSql();

        System.out.println(sql);


        String updSql = SqlWarp.builder(SqlWarp.SqlType.UPDATE).table("t_user_inf")
                .set("name", "'张三'")
                .set("age", "10")
                .set("address", "'武汉市洪山区'")
                .where(" id = 2")
                .buildSql();

        System.out.println(updSql);



    }
}

在这里插入图片描述

缺陷部分

当我们使用查询(select)方法的时候,我们也可以调用 set方法,实际上,该方法是给update 使用的,那么有没有一种方式是,如果是 select 我只有我需要的那部分 api 其他的不对外开放,那么下一部分就是优化该部分

String sql = SqlWarp.builder(SqlWarp.SqlType.SELECT)
        .select("name", "age", "address")
        .set("aa","111")
        .table("user")
        .where(" age = 1")
        .buildSql();

v2

优化查询不能调用 set 方法

代码部分
public class SQL {

    private SQL() {}


    public static SelectBuilder select(String ...column) {
        return new SelectBuilder(column);
    }

    public static UpdateBuilder update() {
        return new UpdateBuilder();
    }


    /**
     *  查询 builder
     */
     static class SelectBuilder {

        // 要查询的列
        private final String[] columns;

        //所属表
        private String table;

        //where 条件
        private String where;

        private SelectBuilder(String[] columns) {
            this.columns = columns;
        }


        public SelectBuilder from(String table) {
            this.table = table;
            return this;
        }

        public SelectBuilder where(String where) {
            this.where = where;
            return this;
        }

        public String buildSql() {
            StringBuilder sql = new StringBuilder();
            sql.append(" SELECT ").append(String.join(",",columns)).append(" FROM ").append(table);
            if (where != null) {
                sql.append(" WHERE ").append(where);
            }
            return sql.toString();
        }
    }


    //更新
    public static class UpdateBuilder {

        // 设置列的条件
        private Map<String,String> setMap = new LinkedHashMap<>();

        //表名
        private String table;

        //where 条件
        private String where;


        private UpdateBuilder() {

        }

        public UpdateBuilder table(String table) {
            this.table = table;
            return this;
        }

        public UpdateBuilder set(String key,String value) {
            setMap.put(key,value);
            return this;
        }

        public UpdateBuilder where(String where) {
            this.where = where;
            return this;
        }

        public String buildSql() {
            StringBuilder sql = new StringBuilder();
            sql.append(" UPDATE ").append(table).append(" SET ")
               .append(setMap.entrySet().stream()
                    .map(entry -> entry.getKey() + "=" + entry.getValue())
                    .collect(Collectors.joining(",")));
            if (where != null) {
                sql.append(" WHERE ").append(where);
            }
            return sql.toString();
        }
    }

}
使用部分
public class SQLTest {

    public static void main(String[] args) {
        //查询
        String sql = SQL
                .select("name", "age", "address")
                .from("t_user_inf")
                .where("name = 'zhangsan'")
                .buildSql();
        System.out.println("查询sql:"+sql);


        String updSql = SQL.update()
                .table("t_user_inf")
                .set("name", "zhangsan")
                .set("age", "10")

                .where("name = 'lisi'")
                .buildSql();
        System.out.println("更新sql:"+updSql);

    }

在这里插入图片描述

缺陷部分

虽然我们限制了 查询不能使用set方法,但是,如果我们可以同时设置多个 from 方法,多个where 方法,不能称之为一次性对象,下一节使用接口实现,最终版的 dsl 方式DSL(领域特定语言)

V3

  • 查询实现
  • 修改的实现
  • 删除的实现
查询dsl
  • 查询需要设置的值,定义一套接口
代码部分
public class SqlSelect {

    private SqlSelect() {}


    public static TableStage select() {
        return new SelectBuilder();
    }

    static class SelectBuilder implements TableStage,ColumnStage,WhereStage {

        // 要查询的列
        private String[] columns;

        // 要查询的表
        private String table;

        // where 条件
        private String where;


        @Override
        public WhereStage column(String... columns) {
            this.columns = columns;
            return this;
        }

        @Override
        public ColumnStage from(String table) {
            this.table = table;
            return this;
        }

        @Override
        public WhereStage where(String where) {
            this.where = where;
            return this;
        }

        @Override
        public String buildSql() {
            StringBuilder sql = new StringBuilder();
            sql.append(" SELECT ").append(String.join(",",columns)).append(" FROM ").append(table);
            if (where != null) {
                sql.append(" WHERE ").append(where);
            }

            return sql.toString();
        }
    }



    // table 策略
    interface TableStage {

        ColumnStage from(String table);
    }

    // 查询的列
    interface ColumnStage {

        WhereStage column(String ...column);
    }

    // where 条件
    interface WhereStage {

        WhereStage where(String where);

        String buildSql();

    }

}
使用部分
public class TestMain {


    public static void main(String[] args) {
         String updSql = SqlSelect.select().from("t_user_inf")
                .column("name", "age", "address")
                .where(" id = 6")
                .buildSql();

    }
}
修改dsl
  • 接口部分
  • 接口实现
  • 调用部分
接口部分
public class SQLStrategy {



    // 查询 table , update table set aa where
     interface TableStrategy {


        SetStrategy table(String tableName);

    }



    // 设置列 stage
    interface SetStrategy {


        SetStrategy set(String name,String value);


        WhereStrategy where(String where);
    }



    // where条件
    interface WhereStrategy {

        String buildSql();
    }



}
接口实现
public class SQL {

    private SQL() {}

    public static SQLStrategy.TableStrategy update() {
        return new UpdateBuilder();
    }

    public static SqlDelete.TableStrategy delete() {
        return new SqlDelete.SqlDeleteBuilder();
    }

    static class UpdateBuilder implements SQLStrategy.TableStrategy, SQLStrategy.SetStrategy, SQLStrategy.WhereStrategy {

        private String[] columns;

        private Map<String,String> setMap = new LinkedHashMap<>();

        private String where;

        private String table;


       @Override
       public SQLStrategy.SetStrategy table(String table) {
           this.table = table;
           return this;
       }

       @Override
       public SQLStrategy.SetStrategy set(String name, String value) {
           setMap.put(name,value);
           return this;
       }

       @Override
       public SQLStrategy.WhereStrategy where(String where) {
           this.where = where;
           return this;
       }

       @Override
       public String buildSql() {
           StringBuilder sql = new StringBuilder();
           sql.append(" UPDATE ").append(table).append(" SET ");
           sql.append(setMap.entrySet().stream()
                   .map(entry -> entry.getKey() + "=" + entry.getValue())
                   .collect(Collectors.joining(",")));
           if (where != null) {
               sql.append(" WHERE ").append(where);
           }
           return sql.toString();
       }


   }
}
使用部分
 String sql = SQL.update()
                .table("t_user_inf")
                .set("name", "'张三'")
                .set("age", "12")
                .where("where id = 15")
                .buildSql();

        System.out.println(sql);
删除dsl
  • 蜜封属性
代码部分
public class SqlDelete {

    private SqlDelete() {

    }


    public static TableStrategy delete() {
        return new SqlDeleteBuilder();
    }


    public static class SqlDeleteBuilder implements TableStrategy,WhereStrategy {

        private String table;

        private String where;


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

        @Override
        public WhereStrategy where(String where) {
            this.where = where;
            return this;
        }

        @Override
        public String buildSql() {
            StringBuilder sql = new StringBuilder();
            sql.append(" delete from ").append(table).append(" WHERE ").append(where);
            return sql.toString();
        }
    }




    // 查询 table , update table set aa where
    interface TableStrategy {

        WhereStrategy table(String tableName);

    }



    // where条件
    interface WhereStrategy {

        WhereStrategy where(String where);

        String buildSql();
    }

}
使用部分
 String delSql = SQL.delete()
                .table("t_user_inf").where(" name = '张三'").buildSql();
        System.out.println(delSql);

dsl 输出结果

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到