建造者模式(Builder)---创建型
1 定义域特征
定义:将一个复杂的对象构建与其表示分离,使得同样的构建过程可以创建不同的表示。特征:用户只需要指定需要建造的类型即可,对于中间的细节不考虑。
本质:分离整体构建算法和部件构造。构建一个复杂的对象,本来就有构建的过程,以及构建过程中具体的实现。生成器模式就是用来分离这两个部分,从而使得程序结构更松散、扩展更容易、复用性更好,同时也会使得代码更清晰,意图更明确。
虽然在生成器模式的整体构建算法中,会一步一步引导 Builder来构建对象,但这不是说生成器主要就是用来实现分步骤构建对象的。生成器模式的重心还是在于分离整体构建算法和部件构造,而分步构建对象不过是整体构建算法的一个简单表现,或者说是一个附带产物。
优点:
(1)松散耦合
生成器模式可以用同一个构建算法构建出表现上完全不同的产品,实现产品构建和产品表现上的分离。生成器模式正是把产品构建的过程独立出来,使它和具体产品的表现松散耦合,从而使得构建算法可以复用,而具体产品表现也可以灵活地、方便地扩展和切换。
(2)可以很容易地改变产品的内部表示
在生成器模式中,由于 Builder i对象只是提供接口给 Director使用,那么具体的部件创建和装配方式是被 Builder接口隐藏了的, Director并不知道这些具体的实现细节。这样一来,要想改变产品的内部表示,只需要切换 Builder的具体实现即可,不用管 Director,因此变得很容易
(3)更好的复用性
生成器模式很好地实现了构建算法和具体产品实现的分离。这样一来,使得构建产品的算法可以复用。同样的道理,具体产品的实现也可以复用,同一个产品的实现,可以配合不同的构建算法使用
使用场景:如果创建对象的算法,应该独立于该对象的组成部分以及它们的装配方式时。如果同一个构建过程有着不同的表示时。
2 代码实现及分析
场景:假设需要创建一个文档,该文档可以是txt、html形式的单独或者组合一起实用的,后续可能还会添加excel和word格式的。
Builder类:抽象类定义了创建一个文档所用到的所有方法
public abstract class Builder { //创建一个文档标题,可能还会有很多方法这里就先写一个 public abstract void makeTitle(String title); }
HtmlBuilder类:创建一个Html文档
public class HtmlBuilder extends Builder{ public void makeTitle(String title){ System.out.println("HtmlBuilder: " + title); } //HtmlBuilder内部所有特有的方法 public void make(){ System.out.println("HtmlBuilder特有方法"); } }
TxtBuilder类:创建一个txt文档
public class TxtBuilder extends Builder { public void makeTitle(String title){ System.out.println("TxtBuilder:" + title); } //TxtBuilder内部所有特有的方法 public void make(){ System.out.println("TextBuilder特有方法"); } }
Director类:负责实例传递并进行调用
public class Directer { private Builder builder; //set方式注入对象 public void setBuilder(Builder builder){ this.builder = builder; } public void makeTitle(String title){ builder.makeTitle(title); } }
对于Director类是并不知道具体是由谁来进行实现的,因为是在应用层进行的控制。正是因为只有不知道子类才能进行替换,所以在Director类中实现了可替换性。因为可替换所以在应用层才能进行随意的切换调用,此外基于这个原因在Director类中是不能调用文档类的特有方法,在这里可以认为文档类的特有方法是文档内部编写所需要的,而在Director类中是并不关心内部是如何完成的。
Test类:应用层 指定创建什么样式的文档。
public class Test { public static void main(String[] args) { Directer directer = new Directer(); //让TxtBuilder创建文档 TxtBuilder textBuilder = new TxtBuilder(); directer.setBuilder(textBuilder); directer.makeTitle("创建文档"); //让HtmlBuilder创建文档 HtmlBuilder htmlBuilder = new HtmlBuilder(); directer.setBuilder(htmlBuilder); directer.makeTitle("创建文档"); } }
UML图:
下面这个场景是最初写的样例,感觉没有第一个场景样例更为贴切。
场景:假设一个课程有许多部分组成:PPT、视频、手记、问答,需要在应用层根据不同要求创建不同的课程并设置相应的组成部分。
course类:
//课程类 public class Course { private String courseName; private String coursePPT; private String courseVideo; //笔记 private String courseArticle; //question & answer private String courseQA; public String getCourseName() { return courseName; } public void setCourseName(String courseName) { this.courseName = courseName; } public String getCoursePPT() { return coursePPT; } public void setCoursePPT(String coursePPT) { this.coursePPT = coursePPT; } public String getCourseVideo() { return courseVideo; } public void setCourseVideo(String courseVideo) { this.courseVideo = courseVideo; } public String getCourseArticle() { return courseArticle; } public void setCourseArticle(String courseArticle) { this.courseArticle = courseArticle; } public String getCourseQA() { return courseQA; } public void setCourseQA(String courseQA) { this.courseQA = courseQA; } @Override public String toString() { return "Course{" + "courseName='" + courseName + '\'' + ", coursePPT='" + coursePPT + '\'' + ", courseVideo='" + courseVideo + '\'' + ", courseArticle='" + courseArticle + '\'' + ", courseQA='" + courseQA + '\'' + '}'; } }
CourserBuilder:抽象的课程建造者
/** * 课程建造者 */ public abstract class CourseBuilder { public abstract void buildCourseName(String courseName); public abstract void buildCoursePPT(String coursePPT); public abstract void buildCourseVideo(String courseVideo); public abstract void buildCourseArticle(String courseArticle); public abstract void buildCourseQA(String courseQA); //制作一个课程 public abstract Course makeCourse(); }
CourserActualBuilder:
/** * 课程的真实构建类 */ public class CourseActualBuilder extends CourseBuilder { private Course course = new Course(); @Override public void buildCourseName(String courseName) { course.setCourseName(courseName); } @Override public void buildCoursePPT(String coursePPT) { course.setCoursePPT(coursePPT); } @Override public void buildCourseVideo(String courseVideo) { course.setCourseVideo(courseVideo); } @Override public void buildCourseArticle(String courseArticle) { course.setCourseArticle(courseArticle); } @Override public void buildCourseQA(String courseQA) { course.setCourseQA(courseQA); } @Override public Course makeCourse() { return course; } }
Coach:辅助类
/** * 课程教练,通过接收到的课程信息来构建课程。 */ public class Coach { 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.buildCourseName(courseName); this.courseBuilder.buildCoursePPT(coursePPT); this.courseBuilder.buildCourseVideo(courseVideo); this.courseBuilder.buildCourseArticle(courseArticle); this.courseBuilder.buildCourseQA(courseQA); return this.courseBuilder.makeCourse(); } }
Test类:应用层
/** * 测试类 */ public class Test { public static void main(String[] args) { //多态父类引用子类实现 CourseBuilder courseBuilder = new CourseActualBuilder(); Coach coach = new Coach(); //在coach中注入courseBuilder coach.setCourseBuilder(courseBuilder); Course course = coach.makeCourse("Java设计模式精讲", "Java设计模式精讲PPT", "Java设计模式精讲视频",
"Java设计模式精讲手记","Java设计模式精讲问答");
System.out.println(course); } }
在应用层中通过向coach传递参数来构建一个具体的对象,coach和CourserBuilder为组合的关系,1对1。CourserActualBuilder与Course同样为组合的1对1关系,其类图关系如下图所示。
3 优化
对于上面的代码,coach不是必须的可以让应用层直接控制,采用静态内部类进行改善。
Course类:
public class Course { private String courseName; private String coursePPT; private String courseVideo; private String courseArticle; //question & answer private String courseQA; //这里的CourseBuilder就是静态内部类返回的对象 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对象 public static class CourseBuilder{ private String courseName; private String coursePPT; private String courseVideo; private String courseArticle; //question & answer private String courseQA; //返回对象本身可以让应用层链式调用 public CourseBuilder buildCourseName(String courseName){ this.courseName = courseName; return this; } public CourseBuilder buildCoursePPT(String coursePPT) { this.coursePPT = coursePPT; return this; } public CourseBuilder buildCourseVideo(String courseVideo) { this.courseVideo = courseVideo; return this; } public CourseBuilder buildCourseArticle(String courseArticle) { this.courseArticle = courseArticle; return this; } public CourseBuilder buildCourseQA(String courseQA) { this.courseQA = courseQA; return this; } public Course build(){ return new Course(this); } } @Override public String toString() { return "Course{" + "courseName='" + courseName + '\'' + ", coursePPT='" + coursePPT + '\'' + ", courseVideo='" + courseVideo + '\'' + ", courseArticle='" + courseArticle + '\'' + ", courseQA='" + courseQA + '\'' + '}'; } }
Test类:
public class Test { public static void main(String[] args) { //链式调用 Course course = new Course.CourseBuilder().buildCourseName("Java设计模式精讲").buildCoursePPT("Java设计模式精讲PPT").buildCourseVideo("Java设计模式精讲视频").build(); System.out.println(course); } }
这样其类比上部分要简化很多,其关系图如下:
4 源码分析
(1)JDK源码
StringBuilder类中的append方法:
public StringBuilder append(StringBuffer sb) { super.append(sb); return this; } @Override public StringBuilder append(CharSequence s) { super.append(s); return this; }
可以看出与优化部分的代码类似StringBuilder在建造时没有采用过多的抽象而是采用链式方法直接进行修改。同样的还有StringBuffer,加了关键字synchronized保证了其线程安全
@Override public synchronized StringBuffer append(Object obj) { toStringCache = null; super.append(String.valueOf(obj)); return this; } @Override public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; }
(2)mybatis源码
在SqlSessionFactoryBuilder类中的build方法:根据传入的confi构建默认的SqlSession
public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
寻找Configuration具体怎么来的,首先在SqlSessionFactoryBuilder类中有如下代码:
//在XMLConfigBuilder完成复杂创建的基础上,做一层简单的封装,这里用建造者来包装一层建造者 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { SqlSessionFactory var5; try { //在建造者模式中使用建造者,XMLConfigBuilder负责完成复杂流程的创建 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); var5 = this.build(parser.parse()); } catch (Exception var14) { throw ExceptionFactory.wrapException("Error building SqlSession.", var14); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException var13) { ; } } return var5; }
在上述代码中深入:var5 = this.build(parser.parse());深入parse()方法在XMLConfigBuilder类中有:此时得到了Configuration
public Configuration parse() { if (this.parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } else { this.parsed = true; //调用方法 this.parseConfiguration(this.parser.evalNode("/configuration")); return this.configuration; } }
继续深入:this.parseConfiguration方法便可达到复杂配置对象配置流程,至此建造者模式套用建造者模式的流程全部结束。
//XNode根节点,完成复杂流程的封装工作 private void parseConfiguration(XNode root) { try { Properties settings = this.settingsAsPropertiess(root.evalNode("settings")); this.propertiesElement(root.evalNode("properties")); this.loadCustomVfs(settings); this.typeAliasesElement(root.evalNode("typeAliases")); this.pluginElement(root.evalNode("plugins")); this.objectFactoryElement(root.evalNode("objectFactory")); this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); this.reflectorFactoryElement(root.evalNode("reflectorFactory")); this.settingsElement(settings); this.environmentsElement(root.evalNode("environments")); this.databaseIdProviderElement(root.evalNode("databaseIdProvider")); this.typeHandlerElement(root.evalNode("typeHandlers")); this.mapperElement(root.evalNode("mappers")); } catch (Exception var3) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3); } }
5 相关设计模式
(1)生成器模式和工厂方法模式
这两个模式可以组合使用。生成器模式的 Builder实现中,通常需要选择具体的部件实现。一个可行的方案就是实现成为工厂方法,通过工厂方法来获取具体的部件对象,然后再进行部件的装配。
(2)生成器模式和抽象工厂模式
区别:抽象工厂模式的主要目的是创建产品簇,这个产品簇里面的单个产品就相当于是构成一个复杂对象的部件对象,抽象工厂对象创建完成后就立即返回整个产品簇;而生成器模式的主要目的是按照构造算法,一步一步来构建一个复杂的产品对象,通常要等到整个构建过程结束以后,才会得到最终的产品对象。
事实上,这两个模式是可以组合使用的。在生成器模式的 Builder实现中,需要创建各个部件对象,而这些部件对象是有关联的,通常是构成一个复杂对象的部件对象。也就是说, Builder实现中,需要获取构成一个复杂对象的产品簇,那自然就可以使用抽象工厂模式来实现。这样一来,由抽象工厂模式负贵了部件对象的创建, Builder实现里面则主要负责产品对象整体的构建了。
(3)生成器模式和模板方法模式
模板方法模式主要是用来定义算法的骨架,把算法中某些步骤延退到子类中实现。再想想生成器模式,Director(coach)用来定义整体的构建算法,把算法中某些涉及到具体部件对象的创建和装配的功能,委托给具体的 Builder来实现。虽然生成器不是延迟到子类,是委托给 Builder,但那只是具体实现方式上的差别,从实质上看两个模式很类似,都是定义一个固定的算法骨架,然后把算法中的某些具体步骤交给其他类来完成,都能实现整体算法步骤和某些具体步骤实现的分离。
当然这两个模式也有很大的区别,首先是模式的目的,生成器模式是用来构建复杂对象的,而模板方法是用来定义算法骨架,尤其是一些复杂的业务功能的处理算法的骨架;其次是模式的实现,生成器模式是采用委托的方法,而模板方法采用的是继承的方式;另外从使用的复杂度上,生成器模式需要组合Director(coach)和 Builder对象,然后才能开始构建,要等构建完后才能获得最终的对象,而模板方法就没有这么麻烦,直接使用子类对象即可。
(4)生成器模式和组合模式
这两个模式可以组合使用。对于复杂的组合结构,可以使用生成器模式来一步一步构建
0