设计模式学习笔记——建造者模式

 

定义

将复杂对象与其表示分离容许相同的构建过程建立不一样的表示。前端

用户只须要指定要构建的类型来获取它们,而且不须要知道构建过程和细节。java

这就是如何逐步构建具备多个组件的对象。相同的施工工艺能够产生不一样的产品,这更适合于固定工艺,但不必定是固定顺序web

类型

建立型spring

使用场景

若是一个对象具备很是复杂的内部结构(许多属性);apache

但愿分离复杂对象的建立和使用编程

优势

良好的封装性、制做和使用的分离性; 后端

良好的可扩展性,构造类之间的独立性,在必定程度上解耦session

缺点

产生多余的Builder对象;app

产品内部发生变化,建造者都要修改框架

和工厂模式的区别

建造者模式和工厂模式比较相近,但仍是有区别的:

业务场景:建造在线学习网站的视频教学课程,就好比建造Java课程。
代码实现

首先新建builder包:

建立课程实体类Course,给这个课程设置些属性,设置get/set方法以及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(); } 

而后,使用抽象构建器,咱们须要实现一个真正的课程构建器,课程实际构建器,以继承课程构建器。实现方法:

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 类:

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。这与抽象的建筑师或特定的课程无关,可是与助手无关。测试建立助手。助手以组合的方式使用CourseBuilder类,可是在这种状况下,实际的构建器CourseActual Builder用于建立Course。最后,考试经过这个助手获得具体的课程。

如今有一个CourseBuilder的继承实现类,Test负责建立特定的Builder,而后有不少不一样的Builder,每一个Builder都有不一样的特性,好比前端过程Builder,它也须要前端资源(图片等),全部的应用层均可以根据t.o不一样新事物的实际需求。将特定构建器注入到助手的责任如今交给Test了。

还有另外一种方式,例如,这种教学是后端课程教学(如JavaEE Advanced),不须要前端课程图片和其余资源,那么后端课程的构建者默承认以注入到负责后端课程的教学助理中,从而应用程序层不须要关心。关于特定的构建器(没有新的课程实际构建器),应用层。它只是关于特定的课程助理。

代码实现演进

​ 上面代码实现中引入了一个助理类,但这个助理类不是必须的

​ 在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方法。

接下来完成剩下的建造属性的方法:

 

 

咱们是要经过CourseBuilder返回一个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的地方返回时,将调用此方法。这是在构建器模式中使用构建器。解析器是XMLConfigBuilder的类型。而后调用他的解析方法并输入解析方法:

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

 

 

这里的代码很是清晰,主要负责建立和组装各类组件的配置,从上到下是组装过程。这意味着XML Config Builder主要负责建立复杂对象的配置。SqlSession Factory Builder只作了一个简单的封装层,用一个构建器包装一个构建器层。

相关文章
相关标签/搜索