首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >23天读懂23种设计模式:建造者模式(创建型)

23天读懂23种设计模式:建造者模式(创建型)

作者头像
后台技术汇
发布2022-05-28 12:57:27
发布2022-05-28 12:57:27
45900
代码可运行
举报
文章被收录于专栏:后台技术汇后台技术汇
运行总次数:0
代码可运行

创建型模式是用来创建对象的模式,抽象了实例化的过程,帮助一个系统独立于其他关联对象的创建、组合和表示方式。

建造者模式目的:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示

winter

建造者模式也是创建型的设计模式之一,本文是设计模式系列(共24节)的第5篇文章。

设计模式都从六大原则出发进行总结:《第一节:设计模式的六大原则

创建型设计模式共5种:

  • 单例模式(Singleton Pattern):一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
  • 工厂方法模式(Factory Pattern):在工厂方法模式中,工厂类成为了抽象类,实际的创建工作将由其具体子类来完成。
  • 抽象工厂模式(Abstract Factory):抽象工厂可以向客户提供一个接口,创建多个产品族中的产品对象,强调的是“对象系列”的变化。
  • 建造者模式(Builder Pattern):把构造对象实例的逻辑移到了类的内部,在类的外部定义了该类的构造逻辑,强调的是产品的构造过程。
  • 原型模式(Prototype Pattern):原型模式和工厂模式一样,同样对客户隐藏了对象创建工作具体的实现细节,通过复制一个现有的对象生成新对象。

建造者模式是什么

建造者模式解决问题:开发过程中,我们经常有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

建造者模式的应用

下面我们复习下建造者模式的应用示例。

举例子1:我们使用建造者模式解决一个问题:利用建造器反馈多种驱动开发的模式。(驱动方法都是有顺序拼接的步骤,汇聚一起形成软件开发的设计思想)

代码语言:javascript
代码运行次数:0
运行
复制
当前有哪些驱动方法作为软件开发者的指导思想呢?
领域驱动设计(Domain-Driven Design):
    1.整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(Ubiquitous Language)去理解和消化领域知识,
    2.从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),
    3.并在子领域上建立模型,
    4.再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。
测试驱动开发(Test-Driven Development):
    1.先写功能测试,用户角度描述应用的新功能
    2.功能测试失败后,想办法编写代码让它通过(或者说至少让当前失败的测试通过)此时,使用一个或多个单元测试,定义希望代码实现的效果,保证为应用中的每一行代码(至少)编写一个单元测试
    3.单元测试失败后,编写最少量的应用代码,刚好让单元测试通过。有时,要在第二步和第三步之前多次往复,直到我们觉得功能测试有一点进展为止
    4.再次运行功能测试,看是否通过,或者有没有进展。这一步可能促使我们编写一些新的单元测试和代码等
  • 首先是驱动模式的实体类:
代码语言:javascript
代码运行次数:0
运行
复制
/**
 * <p>
 *     驱动模式抽象类
 * </p>
 */
public abstract class AbstractDrivingMethod {
  protected volatile static List<String> puzzles = new ArrayList();
  protected void addPuzzle(String puzzle){
    puzzles.add(puzzle);
  }
  public void listPuzzles(String driveMethodType){
    System.out.println(driveMethodType + ": \n " + StringUtils.join(puzzles,","));
  }
}
public class TDDAbstractDrivingMethod extends AbstractDrivingMethod {

  //添加TDD(测试驱动)的步骤
  public void addTDDDrivingMethodPuzzle(String puzzle) {
    super.addPuzzle(puzzle);
  }
}
public class DDDAbstractDrivingMethod extends AbstractDrivingMethod {

  //添加DDD(领域驱动设计)的步骤
  public void addDDDDrivingMethodPuzzle(String puzzle) {
    super.addPuzzle(puzzle);
  }
}
  • 然后是建造器类 DrivingMethodBuilder:
代码语言:javascript
代码运行次数:0
运行
复制
/**
 * <p>
 *     驱动方法构造器 DrivingMethodBuilder
 * </p>
 */
public class DrivingMethodBuilder {
  //构造DDDDrivingMethod,组件顺序很重要
  public static AbstractDrivingMethod buildDDDDrivingMethod(){
    DDDAbstractDrivingMethod dddDrivingMethod = new DDDAbstractDrivingMethod();
    dddDrivingMethod.addDDDDrivingMethodPuzzle("1.通过 通用语言(Ubiquitous Language)去理解和消化领域知识");
    dddDrivingMethod.addDDDDrivingMethodPuzzle("2.从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域)");
    dddDrivingMethod.addDDDDrivingMethodPuzzle("3.在子领域上建立模型");
    dddDrivingMethod.addDDDDrivingMethodPuzzle("4.再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型");
    return dddDrivingMethod;
  }
  //构造TDDDrivingMethod,组件顺序很重要
  public static AbstractDrivingMethod buildTDDDrivingMethod(){
    TDDAbstractDrivingMethod tddDrivingMethod = new TDDAbstractDrivingMethod();
    tddDrivingMethod.addTDDDrivingMethodPuzzle("1.先写功能测试,用户角度描述应用的新功能");
    tddDrivingMethod.addTDDDrivingMethodPuzzle("2.功能测试失败后,想办法推动开发,开发修改或编写代码让它通过(或者说至少让当前失败的测试通过)");
    tddDrivingMethod.addTDDDrivingMethodPuzzle("3.单元测试失败后,编写最少量的应用代码,刚好让单元测试通过。有时,要在第二步和第三步之前多次往复,直到我们觉得功能测试有一点进展为止");
    tddDrivingMethod.addTDDDrivingMethodPuzzle("4.再次运行功能测试,看是否通过,或者有没有进展");
    return tddDrivingMethod;
  }
}
  • 测试用例:
代码语言:javascript
代码运行次数:0
运行
复制
public class DrivingMethodBuilderDemo {
  public static void main(String[] args) {
    AbstractDrivingMethod dddAbstractDrivingMethod = DrivingMethodBuilder.buildDDDDrivingMethod();
    dddAbstractDrivingMethod.listPuzzles("dddDrivingMethod");

    AbstractDrivingMethod tddAbstractDrivingMethod = DrivingMethodBuilder.buildTDDDrivingMethod();
    tddAbstractDrivingMethod.listPuzzles("tddDrivingMethod");
  }
}

代码解析:

我们通过建造器 DrivingMethodBuilder 对外开放的API(建造器Builder内部封装了各部分组件的拼装逻辑),完成对目标对象的构建。(这里跟之前我们学习过的工厂方法模式的区别是:建造者模式更加关注与零件装配的顺序)

UML图:

建造者模式优缺点

建造者模式一般用于“需要生成的对象具有复杂的内部结构”。

优点是:独立易扩展,便于控制细节风险;

缺点是:建造器内部生产的产品必须有共同点,导致了对象类型有限制;而且随着不断拓展,建造器内部会越来越复杂,甚至有很多的建造类。

建造者模式在JDK源码的应用

举例子2:在JDK源码里,StringBuilder/StringBuffer就是建造者模式的典型实现。

这两个类最主要的区别就是StringBuilder线程不安全,StringBuffer线程安全。 AbstractStringBuilder 实现了 Appendable 接口方法所有append()方法,俨然AbstractStringBuilder 已经是就建造者,只是是一个抽象类AbstractStringBuilder ,不能实例化。

代码语言:javascript
代码运行次数:0
运行
复制
StringBuffer buffer = new StringBuffer();
buffer.append("chars")
        .append(11L)
        .append(11)
        .append(true)
        .append(1.1d)
        .append(1.1f)
        ;
  • StringBuffer 方法列表:
  • StringBuilder方法列表:
  • StringBuffer的API,最终指向建造器 AbstractStringBuilder:
代码语言:javascript
代码运行次数:0
运行
复制
    @Override
    public StringBuilder append(char[] str){
        super.append(str);
        return this;
    }
  • 底层建造器AbstractStringBuilder的API:
代码语言:javascript
代码运行次数:0
运行
复制
    public AbstractStringBuilder append(char[] str) {
        int len = str.length;
        ensureCapacityInternal(count + len);
        System.arraycopy(str, 0, value, count, len);
        count += len;
        return this;
    }

总结

本节主要讲解了建造者模式的实现方法和优缺点,另外补充了jdk源码中的建造者模式应用案例。到此,我们对创建型设计模式的总结还剩下抽象工厂模式,待后续我们再细细解读。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后台技术汇 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 建造者模式是什么
  • 建造者模式的应用
  • 建造者模式优缺点
  • 建造者模式在JDK源码的应用
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档