46 | 建造者模式:详解构造函数、set方法、建造者模式三种对象创建方式
王争
该思维导图由 AI 生成,仅供参考
上两节课中,我们学习了工厂模式,讲了工厂模式的应用场景,并带你实现了一个简单的 DI 容器。今天,我们再来学习另外一个比较常用的创建型设计模式,Builder 模式,中文翻译为建造者模式或者构建者模式,也有人叫它生成器模式。
实际上,建造者模式的原理和代码实现非常简单,掌握起来并不难,难点在于应用场景。比如,你有没有考虑过这样几个问题:直接使用构造函数或者配合 set 方法就能创建对象,为什么还需要建造者模式来创建呢?建造者模式和工厂模式都可以创建对象,那它们两个的区别在哪里呢?
话不多说,带着上面两个问题,让我们开始今天的学习吧!
为什么需要建造者模式?
在平时的开发中,创建一个对象最常用的方式是,使用 new 关键字调用类的构造函数来完成。我的问题是,什么情况下这种方式就不适用了,就需要采用建造者模式来创建对象呢?你可以先思考一下,下面我通过一个例子来带你看一下。
假设有这样一道设计面试题:我们需要定义一个资源池配置类 ResourcePoolConfig。这里的资源池,你可以简单理解为线程池、连接池、对象池等。在这个资源池配置类中,有以下几个成员变量,也就是可配置项。现在,请你编写代码实现这个 ResourcePoolConfig 类。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
本文深入探讨了构造函数、set方法和建造者模式三种对象创建方式,并阐述了它们的优劣势及适用场景。通过资源池配置类的例子引出了建造者模式的应用场景,分析了构造函数和set方法可能存在的问题,并指出了建造者模式的优势,如避免无效状态、集中校验逻辑、创建不可变对象等。文章还对比了工厂模式和建造者模式的区别,强调了理解模式设计的本质和灵活应用的重要性。总之,本文内容简洁明了,对于读者快速了解建造者模式的概念和使用具有很高的参考价值。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《设计模式之美》,新⼈⾸单¥98
《设计模式之美》,新⼈⾸单¥98
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(148)
- 最新
- 精选
- zhengyu.nie依赖关系(Dependencies)、合法校验(Preconditions)、不可变(Immutable)。 争哥这几个描述很精准! 借着思路延伸一下的话,很多库和设计都是以上这些理念的。 比如com.google.guava的校验、不可变集合,多线程设计模式中的Immutable模式、保护性拷贝(其中又可以分深浅api),java.lang.String的不可变设计。还有关于类状态的控制,还有Effective Java中类创建这一章中对于构造方法、set方法、Builder构建、枚举、静态工厂方法构建等对比,像Guava Lists、Sets、Maps、ImmutableList这种创建方式现在也很主流了
作者回复: 👍
2020-04-28244 - 苏暮沉觞小争哥,我在学习建造者模式的时候,发现传统的构建者模式写法跟文中的不太一样,传统的构建者模式存在监工,抽象Builder,具体Builder,产品类。可以通过不同的具体Builder,创建对应的产品类。而你在文章中的设计模式的写法,在某些文章中,被称为变种的构造者模式。 我比较了一下两种方式,感觉传统的构建者模式需要提前定义好你要生产的产品对应的属性,而文中这种方法则是在构建产品时,自己动态的设置产品属性,虽然说这种方法更加灵活,但是侵入性更高。 那么考虑到以后需求变更,是不是应该是用传统的构建者模式呢?不然要是资源池对应的参数变了,就要修改业务代码中的参数了。
作者回复: 在真实的项目里,大部分都是用我说的这种实现方式吧。你说的监工、抽象builder、具体builder的实现方式,有具体的例子吗?
2020-05-214 - 悠南你这建造者模式代码,看不懂,构造方法私有了,怎么来的Builder 方法
作者回复: 内部类可以直接调用外部类的成员和方法,包括private的吧
2020-06-2231 - 是非~成败~/** * 需求: * 当 isRef 为 true 的时候,arg 表示 String 类型的 refBeanId,type 不需要设置; * 当 isRef 为 false 的时候,arg、type 都需要设置。 * 请根据这个需求,完善 ConstructorArg 类。 */ public class ConstructorArg_02 { private boolean isRef; private Class type; private Object arg; private ConstructorArg_02(Builder builder) { this.isRef = builder.isRef; this.type = builder.type; this.arg = builder.arg; } public static class Builder { private boolean isRef; private Class type; private Object arg; // 后面两个参数依赖isRef,所以isRef设置成必选参数 public Builder(boolean isRef) { this.isRef = isRef; } public ConstructorArg_02 build() { return new ConstructorArg_02(this); } public Builder setType(Class type) { this.type = type; return this; } public Builder setArg(Object arg) { // isRef为true, arg不是String类型就报错 if (isRef && arg.getClass() != String.class) { throw new RuntimeException("参数错误"); } this.arg = arg; return this; } } } 咱也不知道写的好不好,希望争哥看到了检视指导一二
作者回复: 不错!
2020-11-16 - Morse为什么定义了一个长方形类,如果不使用建造者模式,采用先创建后 set 的方式,那就会导致在第一个 set 之后,对象处于无效状态?求大佬解答
作者回复: 那就要用builder模式了
2020-08-035 - Darren简单理解就是:工厂模式是根据不同的条件生成不同Class的对象,构建者模式是根据不同参数生成一个class的不同对象。2020-04-104126
- webminpublic class ConstructorArg { private boolean isRef; private Class type; private Object arg; public boolean isRef() { return isRef; } public Class getType() { return type; } public Object getArg() { return arg; } private ConstructorArg(Builder builder) { this.isRef = builder.isRef; this.type = builder.type; this.arg = builder.arg; } public static class Builder { private boolean isRef; private Class type; private Object arg; public ConstructorArg build() { if(isRef && type != null) { throw new IllegalArgumentException("..."); } if (!isRef && type == null) { throw new IllegalArgumentException("..."); } if (this.isRef && (arg != null && arg.getClass() != String.class)) { throw new IllegalArgumentException("..."); } if (!this.isRef && arg == null) { throw new IllegalArgumentException("..."); } return new ConstructorArg(this); } public Builder setRef(boolean ref) { if(ref && this.type != null) { throw new IllegalArgumentException("..."); } this.isRef = ref; return this; } public Builder setType(Class type) { if (this.isRef || type == null) { throw new IllegalArgumentException("..."); } this.type = type; return this; } public Builder setArg(Object arg) { if (this.isRef && (arg != null && arg.getClass() != String.class)) { throw new IllegalArgumentException("..."); } if (!this.isRef && arg == null) { throw new IllegalArgumentException("..."); } this.arg = arg; return this; } } }2020-02-1713114
- 相逢是缘打卡 一、使用场景: 1)类的构造函数必填属性很多,通过set设置,没有办法校验必填属性 2)如果类的属性之间有一定的依赖关系,构造函数配合set方式,无法进行依赖关系和约束条件校验 3)需要创建不可变对象,不能暴露set方法。 (前提是需要传递很多的属性,如果属性很少,可以不需要建造者模式) 二、实现方式: 把构造函数定义为private,定义public static class Builder 内部类,通过Builder 类的set方法设置属性,调用build方法创建对象。 三、和工厂模式的区别: 1)工厂模式:创建不同的同一类型对象(集成同一个父类或是接口的一组子类),由给定的参数来创建哪种类型的对象; 2)建造者模式:创建一种类型的复杂对象,通过很多可设置参数,“定制化”的创建对象2020-02-17455
- javaadu课堂讨论题: /** * 在下面的 ConstructorArg 类中, * 当 isRef 为 true 的时候,arg 表示 String 类型的 refBeanId,type 不需要设置; * 当 isRef 为 false 的时候,arg、type 都需要设置 * * @author javaadu */ public class ConstructorArg { private boolean isRef; private Class type; private Object arg; private ConstructorArg(Builder builder) { this.isRef = builder.isRef; this.type = builder.type; this.arg = builder.arg; } public static class Builder { private boolean isRef; private Class type; private Object arg; public ConstructorArg build() { if (arg == null) { throw new IllegalArgumentException("arg必须设置"); } if (isRef) { if (!(arg instanceof String)) { throw new IllegalArgumentException("arg必须为String类型的对象"); } } else { if (type == null) { throw new IllegalArgumentException("arg必须设置"); } } return new ConstructorArg(this) } public Builder setRef(boolean ref) { isRef = ref; return this; } public Builder setArg(Object arg) { this.arg = arg; return this; } public Builder setType(Class type) { this.type = type; return this; } } }2020-02-1751
- 小文同学说说自己读了 Builder 模式的最大感悟: 1、Builder 模式可以保证对象的状态。 2、Builder 模式可以把对象的构造鉴定逻辑写在Builder类中,保证了类的简洁。 平时普普通通地使用 lombok 生成 Builder,应该更加深入地了解一下。2020-03-0717
收起评论