为了账号安全,请及时绑定邮箱和手机立即绑定

设计模式Java实现-建造者模式

楔子

小七在2019年的时候,就想写一个关于设计模式的专栏,但是最终却半途而废了。粗略一想,如果做完一件事要100分钟,小七用3分钟热情做的事,最少也能完成10件事情了。所以这一次,一定要把他做完,fighting!

需求背景

以以前小七做的一个政务系统为例,为了符合国标,数据库表需要设计很多字段,大概有100多个。每次new这个实体的时候,都会调用大量的set方法,关键是这100个字段基本不会变,但是他们的组合却经常变,弄得开发的小伙伴们苦不堪言,于是前辈们就重载了很多的构造方法,结果构造方法也爆炸了,导致新来的后浪们差点直接被拍死在了沙滩上。

为了简化代码,咱们这一次就定义一个Student类,里面只包含name和age。

分析设计

因为这个对象的属性很多,且组合方式很自由,如果使用经典的new-set方式,代码大概如下:

BigObject bigObject = new BigObject();
bigObject.setO1("");
bigObject.setO2("");
bigObject.setO3("");
bigObject.setO4("");
bigObject.setO5("");
bigObject.setO6("");
bigObject.setO7("");
bigObject.setO8("");
bigObject.setO9("");
bigObject.setO10("");
...
bigObject.setO100("");   

看起来并不直观。

如果每一个组合就重载一个构造方法,也会产生很多构造方法,并且语义不明,新来的小伙伴会一脸懵逼。

但是如果我们能够抽象一下产品的构建过程,具体建造者类继承自抽象建造者类,实现具体的构建逻辑。指挥者类负责调用具体建造者类的构建方法,完成产品的构建。这样就可以降低客户端代码的复杂度,提高代码的可维护性。

定义 类名
产品类 Student
抽象建造者类 StudentBuilder
具体建造者类 StudentActualBuilder
指挥者类 Commander

标准建造者模式

UML图

根据分析设计,我们可以先画一个简单的UML图,后面通过UML图编码

file

模块名称

builder.demo01

模块地址

模块描述

经典模式代码示例

代码实现

1、定义产品类

/**
 * 定义产品类
 * 关注公众号【奔跑的码畜】,一起进步不迷路
 *
 * @author 第七人格
 * @date 2023/11/20
 */
public class Student {
    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;
    }
    
    @Override
    public String toString() {
        return "Student{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
    }
}

2、定义抽象建造者类

/**
 * 定义抽象建造者类
 * 关注公众号【奔跑的码畜】,一起进步不迷路
 *
 * @author 第七人格
 * @date 2023/11/20
 */
public abstract class StudentBuilder {
    
    public abstract void buildName(String name);
    public abstract void buildAge(int age);
    public abstract Student makeStudent();
}

3、定义具体建造者类

/**
 * 定义具体建造者类
 * 关注公众号【奔跑的码畜】,一起进步不迷路
 *
 * @author 第七人格
 * @date 2023/11/20
 */
public class StudentActualBuilder extends StudentBuilder {
    /**
     * 这里使用组合,将 student 组合到实现类中
     */
    private Student student = new Student();

    @Override
    public void buildName(String name) {
        student.setName(name);
    }

    @Override
    public void buildAge(int age) {
        student.setAge(age);
    }

    @Override
    public Student makeStudent() {
        return student;
    }
}

4、定义指挥者类

/**
 * 定义指挥者类
 * 关注公众号【奔跑的码畜】,一起进步不迷路
 *
 * @author 第七人格
 * @date 2023/11/20
 */
public class Commander {
    /**
     * 注入StudentBuilder
     */
    private StudentBuilder studentBuilder;

    public void setStudentBuilder(StudentBuilder studentBuilder) {
        this.studentBuilder = studentBuilder;
    }

    public Student makeStudent(String name, int age) {
        this.studentBuilder.buildAge(age);
        this.studentBuilder.buildName(name);
        return this.studentBuilder.makeStudent();
    }
}

5、测试

public class BuilderStudentBuilderTest {

    @Test
    public void testBuild_01() {
        System.out.println("==========标准建造者模式开始==========");
        StudentActualBuilder studentActualBuilder = new StudentActualBuilder();
        Commander commander = new Commander();
        commander.setStudentBuilder(studentActualBuilder);
        // 客户端使用指挥者类创建产品对象,这样可以降低客户端代码的复杂度,提高代码的可维护性。
        Student student = commander.makeStudent("第七人格", 18);
        System.out.println(student);
        System.out.println("==========标准建造者模式结束==========");
    }
}

6、测试结果

==标准建造者模式开始==

Student{name=‘第七人格’, age=18}

==标准建造者模式结束==

实现要点

  1. 定义产品类:产品类是最终要构建的对象,包含多个属性和方法。

  2. 定义抽象建造者类:抽象建造者类定义了产品的构建过程,包括各个部分的构建方法和返回最终产品的方法。

  3. 定义具体建造者类:具体建造者类继承自抽象建造者类,实现具体的构建逻辑。

  4. 定义指挥者类:指挥者类负责调用具体建造者类的构建方法,完成产品的构建。

用过StringBuilder的我们知道,StringBuilder有个append方法,我们学着StringBuilder将上面的代码,改为链式调用。

链式调用模式

URL图

file

模块名称

builder.demo02

模块地址

模块描述

建造者-链式调用

代码实现

/**
 * 链式调用建造者示例
 * 关注公众号【奔跑的码畜】,一起进步不迷路
 *
 * @author 第七人格
 * @date 2023/11/20
 */
public class Student02Builder {
    /**
     * 姓名
     */
    private String name;
    /**
     * 年龄
     */
    private int age;

    /**
     * 学生类的构造函数
     *
     * @param name 的名字
     * @param age  年龄
     */
    Student02Builder(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 构建器(本质上就是指挥者Commander)
     *
     * @return {@link StudentBuilder}
     */
    public static Student02Builder.StudentBuilder builder() {
        // 构造一个StudentBuilder对象
        return new Student02Builder.StudentBuilder();
    }

    /**
     * 学生构建器(相当于StudentBuilder及其实现类StudentActualBuilder)
     *
     * @author 第七人格
     * @date 2020/12/02
     */
    public static class StudentBuilder {
        private String name;
        private int age;

        public StudentBuilder() {
        }

        public Student02Builder.StudentBuilder name(String name) {
            this.name = name;
            // 返回自身(StudentBuilder),以便链式调用
            return this;
        }

        public Student02Builder.StudentBuilder age(int age) {
            this.age = age;
            // 返回自身(StudentBuilder),以便链式调用
            return this;
        }

        /**
         * 构建
         *
         * @return {@link Student02Builder}
         */
        public Student02Builder build() {
            // 构造一个Student对象,其中的属性直接从外部传入
            return new Student02Builder(this.name, this.age);
        }

        @Override
        public String toString() {
            return "Student.StudentBuilder(name=" + this.name + ", age=" + this.age + ")";
        }
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试

@Test
public void testBuild_02() {
    System.out.println("==========工作中常用-建造者模式开始==========");
    System.out.println(Student02Builder.builder()
            .age(18)
            .name("第七人格")
            .build()
    );
    System.out.println("==========工作中常用-建造者模式开始==========");
}

测试结果

==链式调用-建造者模式开始==

Student{name=‘第七人格’, age=18}

==链式调用-建造者模式开始==

实现要点

1、使用静态方法替换指挥者Commander

public static Student02Builder.StudentBuilder builder() {
    // 构造一个StudentBuilder对象
    return new Student02Builder.StudentBuilder();
}

2、使用内部类替换StudentBuilder及StudentActualBuilder

3、内部类中设置属性的时候,返回自身,以便链式调用

面对对象面对君,不负代码不负卿

点击查看更多内容
2人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消