【设计模式系列11】建造者模式原理和示例及其在jdk,MyBatis源码中的运用

建造者模式分析

  • 设计模式系列总览
  • 前言
  • 什么是建造者模式
    • 建造者模式角色
  • 简单写法示例
    • 1、产品(Product)
    • 2、建造者(ConcreteBuilder)
    • 3、调用者(Director)
  • 标准写法示例
    • 1、产品(Product)
    • 2、抽象建造者(Builder)
    • 3、建造者(ConcreteBuilder)
    • 4、调用者(Director)
  • 适用场景
  • 建造者模式在源码中体现
  • 建造者模式优缺点
  • 建造者模式和工厂模式区别
  • 总结

设计模式系列总览

设计模式 飞机票
三大工厂模式 登机入口
策略模式 登机入口
委派模式 登机入口
模板方法模式 登机入口
观察者模式 登机入口
单例模式 登机入口
原型模式 登机入口
代理模式 登机入口
装饰者模式 登机入口
适配器模式 登机入口
建造者模式 登机入口
责任链模式 登机入口
享元模式 登机入口
组合模式 登机入口
门面模式 登机入口
桥接模式 登机入口
中介者模式 登机入口
迭代器模式 登机入口
状态模式 登机入口
解释器模式 登机入口
备忘录模式 登机入口
命令模式 登机入口
访问者模式 登机入口
软件设计7大原则和设计模式总结 登机入口

前言

当我们创建一个复杂对象时,可能大家的第一反应就是使用工厂模式。但是如果构建一个对象非常复杂,而且有些比如说属性之类的是可选的,而且需要支持我们自己随意的动态搭配,那么这时候如果要用工厂设计模式就不太好实现了,所以这就需要配合我们的建造者模式来实现。

什么是建造者模式

建造者模式(Builder Pattern)是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

建造者模式属于创建型模式,对使用者而言,只需要指定需要建造的类型就可以获得对象,建造过程和细节不需要了解。

建造者模式的概念听上去有点抽象,但是实际上可以这么说,基本上大家都用过,只是可能自己不知道这就是建造者模式而已。后面分析jdk源码中的应用大家就会知道了

建造者模式角色

建造者模式的设计中,主要有4个角色。

  • 产品(Product):要创建的产品对象
  • 抽象建造者(Builder):建造者的抽象类,规范产品对象的各个组成部分的建造,一般由子类实现具体建造过程
  • 建造者(ConcreteBuilder):具体的Builder类,根据不同的业务逻辑,具体到各个对象的各个组成部分的建造
  • 调用者(Director):调用具体的建造者来创建各个对象的各个部分

简单写法示例

我们先来看一个简单的建造者模式写法:

1、产品(Product)

首先我们创建一个产品,我们以家庭作业为例,假设老师会根据每个不同基础的同学布置不同难度的题目为例

package com.zwx.design.pattern.builder;

/**
 * 建造者模式-产品(Product)角色
 */
public class Homework {
     
    private String easyQc;//简答题目

    private String normalQc;//正常题目

    private String MediumQc;//中等难度题目

    private String HardQc;//困难题目

    public String getEasyQc() {
     
        return easyQc;
    }

    public void setEasyQc(String easyQc) {
     
        this.easyQc = easyQc;
    }

    public String getNormalQc() {
     
        return normalQc;
    }

    public void setNormalQc(String normalQc) {
     
        this.normalQc = normalQc;
    }

    public String getMediumQc() {
     
        return MediumQc;
    }

    public void setMediumQc(String mediumQc) {
     
        MediumQc = mediumQc;
    }

    public String getHardQc() {
     
        return HardQc;
    }

    public void setHardQc(String hardQc) {
     
        HardQc = hardQc;
    }
}

2、建造者(ConcreteBuilder)

接下来再创建一个建造者,通过建造者来创建不同的产品

package com.zwx.design.pattern.builder.simple;

import com.zwx.design.pattern.builder.Homework;

/**
 * 建造者模式-具体建造者(ConcreteBuilder)
 */
public class SimpleBuilder {
     
    private Homework homework;

    public SimpleBuilder(Homework homework) {
     
        this.homework = homework;
    }

    public void buildEasyQc(String easyQc){
     
        homework.setEasyQc(easyQc);
    }

    public void buildNormalQc(String normalQc){
     
        homework.setNormalQc(normalQc);
    }

    public void buildMediumQc(String mediumQc){
     
        homework.setMediumQc(mediumQc);
    }

    public void buildHardQc(String hardQc){
     
        homework.setHardQc(hardQc);
    }

    public Homework build(){
     
        return homework;
    }
}

3、调用者(Director)

最后,通过调用者来构建不同的产品

package com.zwx.design.pattern.builder.simple;

import com.alibaba.fastjson.JSONObject;
import com.zwx.design.pattern.builder.Homework;

/**
 * 建造者模式-调用者(Director)
 */
public class SimpleBuilderDirector {
     
    public static void main(String[] args) {
     
        SimpleBuilder simpleBuilder = new SimpleBuilder(new Homework());
        simpleBuilder.buildEasyQc("简单题目");//1
        simpleBuilder.buildNormalQc("标准难度题目");//2
        simpleBuilder.buildMediumQc("中等难度题目");//3
        simpleBuilder.buildHardQc("高难度题目");//4
        Homework homework = simpleBuilder.build();
        System.out.println(JSONObject.toJSONString(homework));
    }
}

这就是一个简单的构造者模式写法,上面的1,2,3,4行代码可以只选择任意1个或者任意搭配的个数,从而创造出独立不同的产品。

这种写法虽然简单,但是因为不是面向抽象编程,导致扩展性不好,所以我们再继续看一下建造者模式标准的写法

标准写法示例

1、产品(Product)

产品类复用上面简单示例的产品类

2、抽象建造者(Builder)

创建一个抽象建造者:

package com.zwx.design.pattern.builder.standard;

import com.zwx.design.pattern.builder.Homework;

/**
 * 建造者模式-抽象建造者(Builder)
 */
public abstract class HomeworkBuilder {
     
    public abstract HomeworkBuilder buildEasyQc(String easyQc);

    public abstract HomeworkBuilder buildNormalQc(String normalQc);

    public abstract HomeworkBuilder buildMediumQc(String mediumQc);

    public abstract HomeworkBuilder buildHardQc(String hardQc);

    public abstract Homework build();
}

这个类定义了5个方法,前面4个是产品类,实际开发中可以任意定义,相当于一个方法就是一个产品中的某一个部分,然后最后一个方法就是返回一个构建好的产品。

3、建造者(ConcreteBuilder)

创建一个实际的建造者角色类:

package com.zwx.design.pattern.builder.standard;

import com.zwx.design.pattern.builder.Homework;

/**
 * 建造者模式-具体建造者(ConcreteBuilder)
 */
public class ConcreateBuilder extends HomeworkBuilder {
     
    private Homework homework;

    public ConcreateBuilder(Homework homework) {
     
        this.homework = homework;
    }

    @Override
    public HomeworkBuilder buildEasyQc(String easyQc) {
     
        homework.setEasyQc(easyQc);
        return this;
    }

    @Override
    public HomeworkBuilder buildNormalQc(String normalQc) {
     
        homework.setNormalQc(normalQc);
        return this;
    }

    @Override
    public HomeworkBuilder buildMediumQc(String mediumQc) {
     
        homework.setMediumQc(mediumQc);
        return this;
    }

    @Override
    public HomeworkBuilder buildHardQc(String hardQc) {
     
        homework.setHardQc(hardQc);
        return this;
    }

    @Override
    public Homework build() {
     
        return homework;
    }
}

这个类里面初始产品我们是通过构造器传进去,实际上也可以不传,通过其他方法初始化也是可以的,这个看需求或者个人喜好。

注意:每个创建部分产品最后的返回值返回的都是this,这样就可以实现链式写法,具体看下面的调用者写法

4、调用者(Director)

最后调用者来创建产品

package com.zwx.design.pattern.builder.standard;

import com.alibaba.fastjson.JSONObject;
import com.zwx.design.pattern.builder.Homework;

/**
 * 建造者模式-调用者(Director)
 */
public class HomeworkDirector {
     
    public static void main(String[] args) {
     
        Homework homework = new Homework();
        HomeworkBuilder homeworkBuilder = new ConcreateBuilder(homework);
        homeworkBuilder.buildEasyQc("我是一道简单题目")
                .buildNormalQc("我是一道标准难度题目")
                .buildMediumQc("我是一道中等难度题目")
                .buildHardQc("我是一道高难度题目");
        homework = homeworkBuilder.build();
        System.out.println(JSONObject.toJSONString(homework));
    }
}

可以看到,这里不用和之前一样一行行去调用,直接通过链式写法一直往下build,最终调用build()方法返回完整的产品对象。

PS:很多地方的写法是把调用者根据不同组合封装起一个个方法供外界调用,这也是一种最严格标准的写法,只适合于排列组合较少结果的产品。如果有很多种搭配,还是目前这种写法比较合适,直接由使用者自己来选择搭配。

适用场景

建造者模式适用于一个具有较多的零件的复杂产品创建过程,而且产品的各个组成零件还会经常发生变化或者说需要支持动态变化,但是零件的种类却总体稳定的场景:

  • 1、相同的方法,不同的执行顺序需要产生不同的执行结果
  • 2、产品类非常复杂,调用不同的零件或者按照不同的顺序来组装产品后需要得到不同的产品
  • 3、当初始化一个对象非常复杂,而且很多参数都具有默认值

建造者模式在源码中体现

建造者模式在源码中使用也很普遍:

  • 1、jdk源码中,StringBuilder类,append方法就是给我们提供了一种链式创建对象的方法:
    【设计模式系列11】建造者模式原理和示例及其在jdk,MyBatis源码中的运用_第1张图片
    然后toString方法返回了一个完整的对象:
    【设计模式系列11】建造者模式原理和示例及其在jdk,MyBatis源码中的运用_第2张图片
  • 2、MyBatis中SqlSessionFactoryBuiler中就用到了建造者模式。
    【设计模式系列11】建造者模式原理和示例及其在jdk,MyBatis源码中的运用_第3张图片
    上面就是构建一个SqlSessionFactory对象的方法,我们看到里面的XMLConfigBuilder其实就是一个具体建造者角色,而其父类BaseBuilder就是一个抽象建造者角色
    下图就是BaseBuilder和其部分子类(为了方便截图,没有显示出所有子类):
    【设计模式系列11】建造者模式原理和示例及其在jdk,MyBatis源码中的运用_第4张图片
    还有一种写法大家应该都非常熟悉的Example,就是经典的链式写法build出来一个完整的sql语句,类似的还有Condition。
    【设计模式系列11】建造者模式原理和示例及其在jdk,MyBatis源码中的运用_第5张图片

建造者模式优缺点

建造者模式的优点有:

  • 1、封装性好,创建和使用分离
  • 2、扩展性好,建造类之间独立,一定程度上实现了解耦

建造者模式的缺点有:

  • 1、产生多余的Builder对象
  • 2、产品内部发生变化时,建造者都需要修改,成本较大

建造者模式和工厂模式区别

建造者模式优点类似于工厂模式,都是用来创建一个对象,但是他们还是有很大的区别,主要区别如下:

  • 1、建造者模式更加注重方法的调用顺序,工厂模式注重于创建完整对象
  • 2、建造者模式根据不同的产品零件和顺序可以创造出不同的产品,而工厂模式创建出来的产品都是一样的
  • 3、建造者模式使用者需要知道这个产品有哪些零件组成,而工厂模式的使用者不需要知道,直接创建就行

总结

本文主要介绍了建造者模式及其在JDK和MyBatis等框架中的具体应用实现,设计模式下一篇,将会介绍责任链模式,责任链模式是一种非常重要的设计模式,应尽可能掌握并熟练使用。
请关注我,和孤狼一起学习进步

你可能感兴趣的:(设计模式,java,设计模式,建造者模式,builder,工厂模式)