Java - GoF设计模式详解3(建造者模式)
作者:hangge | 2023-02-28 08:50
三、建造者模式
1,基本介绍
(1)建造者模式(Builder)可以把复杂对象的创建与表示分离,使得同样的创建过程可以创建不同的表示。建造者模式与抽象工厂模式非常类似,但建造者模式是逐步地构造出一个复杂对象,并在最后返回对象的实例。
(2)该模式中包含的角色及其职责如下:
- Product(产品角色):表示被构造的复杂对象。
- Builder(抽象建造者):为创建一个产品对象的各个部件指定抽象接口。
- ConcreteBuilder(具体建造者):实现 Builder 的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口。
- Director(指挥者):构造一个使用 Builder 接口的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程
2,使用样例
(1)这里我们以肯德基套餐为例演示建造者模式的使用。首先我们创建一个 SetMeal 套餐类,也就是最终要创建的产品类,里面包含主食和饮料两个部件(属性)。
public class SetMeal { private String food; private String drink; public String getFood() { return food; } public void setFood(String food) { this.food = food; } public String getDrink() { return drink; } public void setDrink(String drink) { this.drink = drink; } }
(2)接着创建一个 SetMealBuilder 套餐建造者类(即抽象建造者类),它声明了两个抽象的部件组装方法 buildFood() 和 buildDrink(),同时定义了 SetMeal 产品对象,并提供了工厂方法 getSetMeal() 用于返回 SetMeal 对象。
public abstract class SetMealBuilder { protected SetMeal setMeal = new SetMeal(); public abstract void buildFood(); public abstract void buildDrink(); public SetMeal getSetMeal() { return setMeal; } }
(3)然后创建两个具体的建造者:香辣鸡腿堡套餐建造者、奥尔良烤翅套餐。它们是抽象建造者类的子类,实现了在抽象建造者中声明的部件组装方法,为产品设置不同的部件内容。
//香辣鸡腿堡套餐建造者 public class BurgerSetMealBuilder extends SetMealBuilder{ @Override public void buildFood() { setMeal.setFood("1个香辣鸡腿堡"); } @Override public void buildDrink() { setMeal.setDrink("1杯可乐"); } } //奥尔良烤翅套餐 public class ChickenSetMealBuilder extends SetMealBuilder{ @Override public void buildFood() { setMeal.setFood("1份奥尔良烤翅"); } @Override public void buildDrink() { setMeal.setDrink("1杯九珍果汁"); } }
(4)接着我们创建一个服务员类(即指挥者),其中定义了一个抽象建造者对象,而具体建造者类型由客户端指定;同时在其 construct() 方法中调用建造者对象的部件组装方法和工厂方法,用于向客户端返回1份包含主食和饮料的完整套餐。
public class Waiter { private SetMealBuilder builder; public void setBuilder(SetMealBuilder builder) { this.builder = builder; } public SetMeal construct() { builder.buildFood(); builder.buildDrink(); return builder.getSetMeal(); } }
(5)最后我们测试一下,我们只需要的将对应的套餐建造者对象传入指挥者对象中,然后调用指挥者的 construct() 方法即可自动组装并返回相应的套餐。
public class Test { public static void main(String[] args) { Waiter waiter = new Waiter(); //第一个套餐 SetMealBuilder builder1 = new ChickenSetMealBuilder(); waiter.setBuilder(builder1); SetMeal setMeal1 = waiter.construct(); System.out.println("第一个套餐包含:" + setMeal1.getFood() + "," + setMeal1.getDrink()); //第二个套餐 SetMealBuilder builder2 = new BurgerSetMealBuilder(); waiter.setBuilder(builder2); SetMeal setMeal2 = waiter.construct(); System.out.println("第二个套餐包含:" + setMeal2.getFood() + "," + setMeal2.getDrink()); } }
附一:建造者模式在 JDK 中的应用(StringBuilder、StringBuffer)
(1)JDK 的 StringBuilder 和 StringBuffer 类中提供了 append() 追加方法,这是一种链式创建对象的方法,开放构造步骤,最后调用了 toString() 方法就可获得完整的对象,例如:
提示:相对于 StringBuilder,StringBuffer 的 append 方法加上了 synchronized 关键字。在多线程场景下,使用 StringBuffer 保证线程安全,其他场景则使用 StringBuilder 获得更好的性能
StringBuilder sb = new StringBuilder() .append("1234") .append("abcd") .append("+-*/"); String s = sb.toString();
(2)以 StringBuilder 为例,它的继承结构如下图所示:
- Appendable 接口定义了多个 append 抽象方法,即 Appendable 为抽象建造者。
- AbstractStringBuilder 实现了 Appendable 接口方法,已经是一个具体建造者,但它是一个抽象类不能实例化。
- StringBuilder 继承了 AbstractStringBuilder,它通过调用父类 AbstractStringBuilder 的 append 方法的形式重写了 append 方法。因而,StringBuilder 相当于 Appendable 抽象构建者的具体构建者,同时又充当了指挥者(Director)的角色。
附二:MyBatis 中的建造者模式
(1)MyBatis 中 SqlSessionFactoryBuiler 类用到了建造者模式,SqlSessionFactory 是通过 SqlSessionFactoryBuilder 创建的,代码如下。
public class SqlSessionFactoryBuilder { ... public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); } }
(2)但是,Configuration 类的属性非常多,设置起来非常繁杂。为了简便设置,在 SqlSessionFactoryBuilder 类中有个重载的 build 方法便通过 XMLConfigBuilder 的 parse() 方法构建 Configuration 实例。
public class SqlSessionFactoryBuilder { public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { // 创建建造者 XMLConfigBuilder的 实例 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); // XMLConfigBuilder 的 parse() 创建 Configuration 实例 return build(parser.parse()); } catch (Exception e) { // 捕获异常 throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { // 重置错误 ErrorContext.instance().reset(); try { // 释放资源 inputStream.close(); } catch (IOException e) { // IO 错误不进行处理 } } } }
(3)XMLConfigBuilder 负责 Configuration 各个组建的创建和装配,整个装配流程如下:
package org.apache.ibatis.builder.xml; ... public class XMLConfigBuilder extends BaseBuilder { // 解析配置累 private void parseConfiguration(XNode root) { try { // issue #117 read properties first // 1.读取XML的<properties>标签 propertiesElement(root.evalNode("properties")); // 2. 读取XML的<settings>标签 Properties settings = settingsAsProperties(root.evalNode("settings")); // 3. 加载自定义的配置 loadCustomVfs(settings); loadCustomLogImpl(settings); // 4. 加载XML的<typeAliases> 标签 typeAliasesElement(root.evalNode("typeAliases")); // 5. 加载XML的 <plugins> 标签 pluginElement(root.evalNode("plugins")); // 6. 加载XML的<objectFactory> 标签 objectFactoryElement(root.evalNode("objectFactory")); // 7. 加载XML的<objectWrapperFactory>标签 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); // 8. 加载XML的<reflectorFactory> 标签 reflectorFactoryElement(root.evalNode("reflectorFactory")); // 9. 更新设置 settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 // 10. 加载XML的<environments> 标签 environmentsElement(root.evalNode("environments")); // 11. 加载XML的 <databaseIdProvider> 标签 databaseIdProviderElement(root.evalNode("databaseIdProvider")); // 12. 加载XML的 <typeHandlers> 标签 typeHandlerElement(root.evalNode("typeHandlers")); // 13. 加载XML的 <mappers> 标签 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } } }
(4)我们可以看出:
- XMLConfigBuilder 类负责创建复杂对象 Configuration,XMLConfigBuilder 可以视为具体建造者角色,Configuration 相当于产品。XMLConfigBuilder 继承自 BaseBuilder,所以 BaseBuilder 相当于是抽象建造者的角色。
- 而 SqlSessionFactoryBuilder 则是以封装形式根据不同的输⼊参数构建 SqlSessionFactory 实例,又相当于一个简化的建造者模式。
全部评论(0)