书接上回,本篇讲一下创建型模式-构建者设计模式
构建者设计模式
定义:将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示
这定义一如既往的晦涩难懂,通读之后发现它一直在强调2个点:1>对象构建 2>对象表示,围绕这2个点,再延伸到:1>对象构建过程,2>对象的不同表示。
对象构建:简单理解就是new 对象,当实例化一个非常复杂对象时,该如何设计才能算优雅。
对象构建过程:优雅创建复杂对象实例的过程
实例:
比如使用普通new方式创建对象,
比如使用工厂模式创建对象,
比如:通过反射方式构建对象
等。
对象表示: 构建对象的具体实现步骤细节(方式)
对象不同表示:相同的构建逻辑下(这是前提),创建出不同对象表示
案例:
比如:对象属性很多, 满足某种条件,A属性有值, B属性没值,某种另外种条件,则反过来。
比如:满足某种条件,对象A方法实现A逻辑,满足另外种条件,对象A方法实现B逻辑。
等
上面那些概念, 如果映射到生活的例子可以这么描述:
一条饮料全自动生产流程线,由洗瓶,配料,灌装,包装,成品区域组成,机器可以控制配料、灌装,也可以控制饮料的包装等, 同一条流水线上,根据操作选择不同, 最终饮料成品也各不相同。
一瓶饮料从空瓶到成品为:对象构建, 而整个操作过程可以理解为:对象构建过程。
饮料成品生产细节(比如:如何配料,如何灌装,如何包装等):对象表示,
不同种类的饮料生产细节:对象不同表示
案例分析
需求:使用流水线生产(需要:配料,灌装,包装 步骤)一瓶可乐
饮料-父类
//饮料父类
public class Drink {
private String packing; //牌子
private String ingredient; //配料
private String drink; //饮料类型
public String getPacking() {
return packing;
}
public void setPacking(String packing) {
this.packing = packing;
}
public String getIngredient() {
return ingredient;
}
public void setIngredient(String ingredient) {
this.ingredient = ingredient;
}
public String getDrink() {
return drink;
}
public void setDrink(String drink) {
this.drink = drink;
}
@Override
public String toString() {
return "Drink{" +
"packing='" + packing + '\'' +
", ingredient='" + ingredient + '\'' +
", drink='" + drink + '\'' +
'}';
}
}
可乐-子类
//可乐-饮料子类
public class Cola extends Drink{
}
生产线-接口
//生产线
public interface IBuilder {
//配料
IBuilder ingredientHandler(String ingredient);
//灌装
IBuilder fillDrink(String drink);
//包装
IBuilder packing(String packing);
//生产
Drink build();
}
可乐生产线-接口实现
//可乐生产线
public class ColaBuilder implements IBuilder{
private String ingredient; //配料
private String packing; //瓶子的包装
private String drink; //饮料
@Override
public IBuilder ingredientHandler(String ingredient) {
this.ingredient = ingredient;
return this;
}
@Override
public IBuilder fillDrink(String drink) {
this.drink = drink;
return this;
}
@Override
public IBuilder packing(String packing) {
this.packing = packing;
return this;
}
@Override
public Cola build() {
Cola cola = new Cola();
cola.setPacking(this.packing);
cola.setIngredient(this.ingredient);
cola.setDrink(this.drink);
return cola;
}
}
生产车间
//生产车间
public class ProductionWorkshop {
//生产
private IBuilder builder;
public void setBuilder(IBuilder builder) {
this.builder = builder;
}
public Drink produce(){
return builder.build();
}
}
测试
public class App {
public static void main(String[] args) {
//可乐流水线
ColaBuilder colaBuilder = (ColaBuilder) new ColaBuilder()
.fillDrink("百事可乐")
.ingredientHandler("水, 糖浆,香精")
.packing("pepsi");
ProductionWorkshop pw = new ProductionWorkshop();
pw.setBuilder(colaBuilder);
Drink cola = pw.produce();
System.out.println(cola);
}
}
UML图
分析
结合上面的UML图,再看会构建者模式的定义:
定义:将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示
Cola(Drink)的创建,无非是给packing,ingredient,drink 个属性赋值,最常规的操作无非是new出对象,然后调用set方法给属性赋值。此时你就会发现, new(创建) 跟set(表现) 都在同一个Cola(Drink)中实现,也就是说对象的构建与表示是一体的。、
如果采用上面UML描述的方式设计代码, 你可以看到,最终对象是通过ProductionWorkshop.produce 方法创建而来,而对象的属性赋值通过ColaBuilder类的ingredientHandler,fillDrink ,packing 3个方法实现的。对象的构建与表示分开啦。
这就是构建者模式所表示意思。后续想生产果汁怎么办?答案是:再构建一条果汁生产线
果汁
//果汁
public class FruitJuice extends Drink{
}
果汁生产线
//果汁生产线
public class FruitJuiceBuilder implements IBuilder{
private String ingredient; //配料
private String packing; //瓶子的包装
private String drink; //饮料
@Override
public IBuilder ingredientHandler(String ingredient) {
this.ingredient = ingredient;
return this;
}
@Override
public IBuilder fillDrink(String drink) {
this.drink = drink;
return this;
}
@Override
public IBuilder packing(String packing) {
this.packing = packing;
return this;
}
@Override
public FruitJuice build() {
FruitJuice juice = new FruitJuice();
juice.setPacking(this.packing);
juice.setIngredient(this.ingredient);
juice.setDrink(this.drink);
return juice;
}
}
测试
public class App {
public static void main(String[] args) {
//可乐流水线
ColaBuilder colaBuilder = (ColaBuilder) new ColaBuilder()
.fillDrink("百事可乐")
.ingredientHandler("水, 糖浆,香精")
.packing("pepsi");
ProductionWorkshop pw = new ProductionWorkshop();
pw.setBuilder(colaBuilder);
Drink cola = pw.produce();
System.out.println(cola);
//果汁流水线
FruitJuiceBuilder fruitJuiceBuilder = (FruitJuiceBuilder) new FruitJuiceBuilder()
.fillDrink("汇源果汁")
.ingredientHandler("水, 果汁,香精")
.packing("hiyan");
pw.setBuilder(fruitJuiceBuilder);
Drink juice = pw.produce();
System.out.println(juice);
}
}
模式拓展
标准的构建者模式
UML图
跟案例中UML大同小异,区别是仅仅是名称
Product--产品----Drink
Builder--生成器--IBuilder
ConcreteBuilder---实际生成器---ColaBuilder
Director---指导者---ProductionWorkshop
简化版构建者模式
开发中还有一种简化版的构建者模式,就是去掉指导者:Director,实例构建直接让Builder完成即可。
回到案例代码, 直接调用builder接口中builder方法,不需要经过指导者
//可乐流水线
Drink cola =new ColaBuilder()
.fillDrink("百事可乐")
.ingredientHandler("水, 糖浆,香精")
.packing("pepsi")
.build();
实体对象内置构造器
这种方式也是一种简化方案, 将Builder设置成内部类形式
//可乐-饮料子类
public class Cola extends Drink {
public static class ColaBuilder implements IBuilder{
private String packing; //牌子
private String ingredient; //配料
private String drink; //饮料类型
@Override
public ColaBuilder ingredientHandler(String ingredient) {
this.ingredient = ingredient;
return this;
}
@Override
public ColaBuilder fillDrink(String drink) {
this.drink = drink;
return this;
}
@Override
public ColaBuilder packing(String packing) {
this.packing = packing;
return this;
}
@Override
public Cola build() {
Cola cola = new Cola();
cola.setPacking(this.packing);
cola.setIngredient(this.ingredient);
cola.setDrink(this.drink);
return cola;
}
}
}
测试
public class App {
public static void main(String[] args) {
Cola cola = new Cola.ColaBuilder()
.fillDrink("百事可乐")
.ingredientHandler("水, 糖浆,香精")
.packing("pepsi")
.build();
System.out.println(cola);
}
}
小结
不管是标准的,还是简化版,内置版,只要符合对象创建与表现分离,都是构建者模式
适用场景
1>构建复杂内部构造的对象
2>想把复杂对象创建与使用分离
优缺点
优点:
封装性好,创建与使用分离
扩展性好, 构建类之间相互独立,一定程度上解耦
缺点
产生多余的Builder对象
产品内部发生变化,构建者修改成本高
开发案例
案例1
还是从JDK里面找例子-StringBuilder
String--产品
StringBuilder---构建者
AbstractStringBuilder---父构建者
没有指导者
String sb = new StringBuilder()
.append("a")
.append("b")
.toString();
StringBuilder 简化版的构建者模式
案例2
Spring中BeanDefinitionBuilder 也是一个简单的构建者模式
BeanDefinition--产品
BeanDefinitionBuilder ---构建者
没有父构建者
Spring容器---指导者
public final class BeanDefinitionBuilder {
public static BeanDefinitionBuilder genericBeanDefinition() {
return new BeanDefinitionBuilder(new GenericBeanDefinition());
}
public static BeanDefinitionBuilder genericBeanDefinition(Class<?> beanClass) {
BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new GenericBeanDefinition());
builder.beanDefinition.setBeanClass(beanClass);
return builder;
}
public AbstractBeanDefinition getBeanDefinition() {
this.beanDefinition.validate();
return this.beanDefinition;
}
}
案例3
Mybatis框架中SqlSessionFactoryBuilder 是一个非常经典的构建者模式案例
SqlSessionFactory --- 产品
SqlSessionFactoryBuilder----构建者
没父构建者
客户端程序---指导者
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
public SqlSessionFactory build(Reader reader, String environment) {
return build(reader, environment, null);
}
public SqlSessionFactory build(Reader reader, Properties properties) {
return build(reader, null, properties);
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
}
有意思的是, 里面还套着一个构建者模式-XMLConfigBuilder
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed;
private final XPathParser parser;
private String environment;
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
}
总结
1>构建者模式的本质:分离对象构建与对象的表示。使用时前尽量结合业务实际,是否真的有这个必要,不能为了用而用。
2>构建者模式跟工厂模式主要区别在于构建对象的侧重点:构建者模式强调对象构建过程与表现结果的分离, 工厂在乎构建对象的结果。