内容简介:将一个复杂的对象与它的表示分离,使得同样的构建过程可以创建不同的表示。 用户只需要指定需要建造的类型就可以得到它们,建造过程及细节不需要知道。 【就是如何一步步构建一个包含多个组件的对象,相同的构建过程可以创建不同的产品,比较适用于流程固定但是顺序不一定固定】
将一个复杂的对象与它的表示分离,使得同样的构建过程可以创建不同的表示。
用户只需要指定需要建造的类型就可以得到它们,建造过程及细节不需要知道。
【就是如何一步步构建一个包含多个组件的对象,相同的构建过程可以创建不同的产品,比较适用于流程固定但是顺序不一定固定】
类型
创建型
使用场景
如果一个对象有非常复杂的内部结构(很多属性);
想把复杂的对象的创建和使用进行分离
优点
封装性好,创建和使用分离;
扩展性好、建造类之间独立,一定程度上解耦
缺点
产生多余的Builder对象;
产品内部发生变化,建造者都要修改
和工厂模式的区别
建造者模式和工厂模式比较相近,但还是有区别的:
不同点 | 建造者模式 | 工厂模式 |
---|---|---|
着重点 | 更注重方法的调用顺序 | 注重创建产品 |
创建力度 | 可以创建一些复杂的产品 | 创建的都是一个模样 |
关注点 | 不止创造这个产品,还要知道产品的组成部件 | 创建出想要的对象就行 |
代码实现
业务场景:建造在线学习网站的视频教学课程,就比如建造 Java 课程。
首先新建builder包:
创建课程实体类Course,给这个课程设置些属性,设置get/set方法以及toString:
package com.ljm.design.pattern.creational.builder; /** * 课程实体类 */ public class Course { //为了方便全使用String,因为这不是重点 private String courseName;//名字 private String coursePPT;//PPT private String courseVideo;//视频 private String courseArticle;//文章 private String courseQA;//问答 //为节省空间,set/get/toString省略 } 复制代码
接下来创建一个抽象类。这个类是课程的建造者,然后根据课程类Course的属性声明他们的建造方法,最后再声明一个构造课程整体的抽象方法:
package com.ljm.design.pattern.creational.builder; //抽象的课程建造者 public abstract class CourseBuilder { public abstract void builderCourseName(String courseName); public abstract void builderCoursePPT(String coursePPT); public abstract void builderCourseVideo(String courseVideo); public abstract void builderCourseArticle(String courseArticle); public abstract void builderCourseQA(String courseQA); //属性就建造完成后,建造课程并返回 public abstract Course makeCourse(); } 复制代码
接下有了抽象建造者,就要来实现一个真正的课程建造者CourseActualBuilder继承CourseBuilder。,实现里面的方法:
package com.ljm.design.pattern.creational.builder; //课程的实际建造者 public class CourseActualBuilder extends CourseBuilder { //这里简单实现以下,直接设置属性,最后返回 private Course course = new Course(); @Override public void builderCourseName(String courseName) { course.setCourseName(courseName); } @Override public void builderCoursePPT(String coursePPT) { course.setCoursePPT(coursePPT); } @Override public void builderCourseVideo(String courseVideo) { course.setCourseVideo(courseVideo); } @Override public void builderCourseArticle(String courseArticle) { course.setCourseArticle(courseArticle); } @Override public void builderCourseQA(String courseQA) { course.setCourseQA(courseQA); } @Override public Course makeCourse() { return course; } } 复制代码
在这里引入一个课程助理Assistant ,课程讲师和学习网站合作时,网站老板肯定不会和讲师谈业务,而是会指派一个业务人员和讲师对接,那这个人可以称之为课程助理。网站老板在下达课程任务,会告诉课程助理,然后助理和对应的讲师进行对接,然后来共同制作这个课程。
这里可以认为助理是一个指挥官,讲师负责课程(提交课程属性),课程助理通过讲师提交的资料拼接成一个完整的课程。
接下来完成Assistant 类:
package com.ljm.design.pattern.creational.builder; //课程助理类 public class Assistant { //助理负责组装课程,可定有CourseBuilder private CourseBuilder courseBuilder; //通过set注入 public void setCourseBuilder(CourseBuilder courseBuilder) { this.courseBuilder = courseBuilder; } //声明组装行为,返回课程 public Course makeCourse(String courseName, String coursePPT, String courseVideo, String courseArticle, String courseQA){ this.courseBuilder.builderCourseName(courseName); this.courseBuilder.builderCoursePPT(coursePPT); this.courseBuilder.builderCourseVideo(courseVideo); this.courseBuilder.builderCourseArticle(courseArticle); this.courseBuilder.builderCourseQA(courseQA); return this.courseBuilder.makeCourse(); } } 复制代码
现在来看看这几个类的UML图:
指挥官也就是助理和课程建造者组合,一个助理包含一个(抽象)课程建造者;实际的建造者包含(持有)一个课程;都是1:1关系。
最后来创建测试类Test:
public class Test { public static void main(String[] args) { //抽象的父类引用来创建子类实现 CourseBuilder courseBuilder = new CourseActualBuilder(); //new一个助理 Assistant assistant = new Assistant(); //注入builder assistant.setCourseBuilder(courseBuilder); //调用助产生课程的方法 Course course = assistant.makeCourse("JavaEE高级","JavaEE高级PPT", "JavaEE高级视频","JavaEE高级文章","JavaEE高级问答"); System.out.println(course); } } 复制代码
再来看一下UML类图:
这里主要看Test,他和抽象的builder和具体的课程都没有关系,但是和助理有关系,Test来创建助理,助理通过组合的方式使用CourseBuilder类,但是着这个例子中实际使用的是实际的建造者CourseActualBuilder来创建Course。最后Test通过这个助理拿到具体的课程类。
现在对于CourseBuilder有一个继承的实现类,而Test负责创建具体的Builder,那么就可以有很多不同的Builder,每个Builder他们特点都不一样,就比如还有个前端课程的Builder,里面还要builder一个前端资源(图片等),所有可以再应用层根据实际需求的不同new出实际建造者传给助理,也就是说注入具体建造者到助理的职责现在交给Test。
还有一种方式,就比如这个教学就是一个后端课程的教学(就比如JavaEE高级),完全不需要前端课程的图片等资源,那么就可以把后端课程的builder默认注入负责后端课程的教学助理当中,这样应用层就不用关心具体的建造者(不用 new CourseActualBuilder),应用层只和具体的课程助理有关。
代码实现演进
上面代码实现中引入了一个助理类,但这个助理类不是必须的
在builder包创建一个文件夹,com.ljm.design.pattern.creational.builder.v2,表示版本2.
先来创建一个课程类Course,这里要使用静态内部类,这个内部类就是建造者,收先还是设置跟前面一样的属性,重写toString方便测试,然后声明一个静态内部类CourseBuilder,静态内部类还是有一样的五个属性,直接写建造每个属性的方法,
package com.ljm.design.pattern.creational.builder.v2; /** * 课程实体类 * v2 */ public class Course { private String courseName;//名字 private String coursePPT;//PPT private String courseVideo;//视频 private String courseArticle;//文章 private String courseQA;//问答 @Override public String toString() { return "Course{" + "courseName='" + courseName + '\'' + ", coursePPT='" + coursePPT + '\'' + ", courseVideo='" + courseVideo + '\'' + ", courseArticle='" + courseArticle + '\'' + ", courseQA='" + courseQA + '\'' + '}'; } //静态内部类:建造者 public static class CourseBuilder{ private String courseName;//名字 private String coursePPT;//PPT private String courseVideo;//视频 private String courseArticle;//文章 private String courseQA;//问答 /** * 建造属性 */ public void builderCourseName(String courseName){ this.courseName = courseName; } } } 复制代码
演进班的核心在于 链式调用 ,所以要把建造属性的方法的返回改成这个静态内部类本身,所以上面的建造属性的方法应该这样写:
/** * 建造属性 */ public CourseBuilder builderCourseName(String courseName){ this.courseName = courseName; return this;//返回的是本身 } 复制代码
这样,返回本身之后就还可以调用其他的builder方法。
接下来完成剩下的建造属性的方法:
我们是要通过CourseBuilde r返回一个Course ,那么在Course类中写一个(空)构造器,但是构造器的参数改为CourseBuilder,而这个参数正式Course的静态内部内CourseBuilder创建的对象:
所以这样我们还要在CourseBuilder类中在写一个方法builder,返回Course:
public Course builder(){ return new Course(this); } 复制代码
然后再来完善一下Course的构造器:
public Course(CourseBuilder courseBuilder) { this.courseName = courseBuilder.courseName; this.coursePPT = courseBuilder.coursePPT; this.courseVideo = courseBuilder.courseVideo; this.courseArticle = courseBuilder.courseArticle; this.courseQA = courseBuilder.courseQA; } 复制代码
这样呢,Course里面的的所有属性就通过CourseBuilder构建成功了
最后再来写一个测试类:
public class Test { public static void main(String[] args) { /** * 这就是链式调用(也叫链式编程)的效果,可以一直调用 * 并且可以选择性调用 * 因为使用Course接收,所以最后要调用CourseBuilder的builder方法 */ Course course = new Course.CourseBuilder().builderCourseName("JavaEE高级") .builderCoursePPT("JavaEE高级PPT").builderCourseVideo("JavaEE高级Video") .builderCourseQA("JavaEE高级QA").builder(); System.out.println(course); } } 复制代码
大家可以和之前的Test的代码对比,感受一下演进版的好处。
再来看一下v2版本的UML类图:
这个图现在非常简单,Test创建Course具体的建造者CourseBuilder,在通过CourseBuilder建造Course。
源码分析
jdk源码:
以java.lang.StringBuilder为例,从这个类名就可以看出他是一个Builder,他的append方法是我们经常用的,里面很多重载:
StringBuffer也是一样的,只不过StringBuffer里面加了同步锁。
Guava源码:
除了jdk,很多开源框架也大量使用建造者模式,Google的开源框架Guava为例,找到avro.shaded.com.google.common.collect.ImmutableSet,这个类本身就是不可变的Set,
里面的copyOf方法返回值也是ImmutableSet
还有add方法:
返回的是ArrayBasedBuilder:
那么这个Builder肯定存在一个builder方法,Ctrl+F12 搜索发现最后确实有一个builder方法:
/** * Returns a newly-created {@code ImmutableSet} based on the contents of * the {@code Builder}. */ @Override public ImmutableSet<E> build() { ImmutableSet<E> result = construct(size, contents); // construct has the side effect of deduping contents, so we update size // accordingly. size = result.size(); return result; } 复制代码
这个就很像我们写的v2版本的代码。
可以实际写一下,还是在v2包的Test代码中写(暂时忽略前面写的):
Set<String> set = ImmutableSet.<String>builder().add("a").add("b").build(); System.out.println(set); 复制代码
Spring源码:
再看一个Spring中的org.springframework.beans.factory.support.BeanDefinitionBuilder:
可以看到它里面的方法返回的都是自己本身。也是一个典型的建造者模式。
Mybatis源码:
看一些Mybatis中对于建造者模式的典型应用:
org.apache.ibatis.session.SqlSessionFactoryBuilder
从名字就可以看出这也是一个Builder,
这个Builder返回的都是SqlSessionFactory,里面还有一个:
这个就是解析Mybatis的xml文件,这里面的核心就是builder方法:
public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); } 复制代码
这个builder传入的是Configuration配置,再把配置传给DefaultSqlSessionFactory进行构造,看一下哪里使用了这个方法(方法名选中,Alt+F7,双击进入):
发现还是在刚刚解析xml的地方,在返回的时候调用了这个方法,这就是在建造者模式中在使用建造者,parser就是XMLConfigBuilder类型,然后调用他的parse方法,进入parse方法:
public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; } 复制代码
而parse方法调用了parseConfiguration,进入parseConfiguration
这里代码就很明白,主要负责Configuration各个组件的创建及装配,从上到下就是装配的流程。那说明XMLConfigBuilder主要负责创建复杂对象的Configuration,SqlSessionFactoryBuilder只不过做了层简单的封装,用建造者包装一层建造者。
以上所述就是小编给大家介绍的《设计模式学习笔记4:建造者模式》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 设计模式——订阅模式(观察者模式)
- 设计模式-简单工厂、工厂方法模式、抽象工厂模式
- java23种设计模式-门面模式(外观模式)
- 设计模式-享元设计模式
- Java 设计模式之工厂方法模式与抽象工厂模式
- JAVA设计模式之模板方法模式和建造者模式
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Programming From The Ground Up
Jonathan Bartlett / Bartlett Publishing / 2004-07-31 / USD 34.95
Programming from the Ground Up is an introduction to programming using assembly language on the Linux platform for x86 machines. It is a great book for novices who are just learning to program as wel......一起来看看 《Programming From The Ground Up》 这本书的介绍吧!