定义
建造者模式是一种对象创建型模式,它将客户端与包含多个部件的复杂对象的创建过程分离,客户端无须知道复杂对象的内部组成部分与装配方式,只需要知道所需建造者的类型即可。建造者模式关注如何一步一步地创建一个复杂对象,不同的建造者定义了不同的创建过程。
模式结构
一个完整的建造者模式一般由Director(指挥者) Builder(抽象建造者) ConcreteBuilder(具体建造者) Product(产品)四部分组成。
UML类图关系如下:
模式结构代码实现
Director:
package com.zhaohy.app.builderModel;
import com.alibaba.fastjson.JSONObject;
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public void setBuilder(Builder builder) {
this.builder = builder;
}
public Product construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
public static void main(String[] args) {
Builder builder = new ConcreteBuilder1();//可通过配置文件用java反射的方式实现
Director director = new Director(builder);
Product product = director.construct();
System.out.println(JSONObject.toJSONString(product));
}
}
Builder:
package com.zhaohy.app.builderModel;
public abstract class Builder {
protected Product product = new Product();
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
public Product getResult() {
return product;
}
}
ConcreteBuilder1:
package com.zhaohy.app.builderModel;
public class ConcreteBuilder1 extends Builder{
@Override
public void buildPartA() {
product.setPartA("A1");
}
@Override
public void buildPartB() {
product.setPartB("B1");
}
@Override
public void buildPartC() {
product.setPartC("C1");
}
}
Product:
package com.zhaohy.app.builderModel;
public class Product {
private String partA;//定义部件,部件可以是任意类型,包括值类型和引用类型
private String partB;
private String partC;
public String getPartA() {
return partA;
}
public void setPartA(String partA) {
this.partA = partA;
}
public String getPartB() {
return partB;
}
public void setPartB(String partB) {
this.partB = partB;
}
public String getPartC() {
return partC;
}
public void setPartC(String partC) {
this.partC = partC;
}
}
如上代码在Director里运行main方法,输出如下:
{"partA":"A1","partB":"B1","partC":"C1"}
可以看到上面在Drector类中可以注入一个抽象Builder类型的对象,它提供了一个建造方法construct(),在该方法中调用了builder对象的构造部件的方法,最后返回一个产品对象。对于客户端而言,只需要关心具体建造者的类型,无须关心产品对象的具体组装过程。
注意
建造者模式与抽象工厂模式都是较为复杂的创建型模式,建造者模式返回一个完整的复杂产品,抽象工厂模式返回一系列相关的产品;在抽象工厂模式中,客户端通过选择具体工厂来生成所需对象,而在建造者模式中,客户端通过指定具体建造者类型来指导Director类如何去生成对象,侧重于一步步构造一个复杂对象,然后将结果返回。如果将抽象工厂模式看成一个汽车配件生产厂,生成不同类型的汽车配件,那么建造者模式就是一个汽车组装厂,通过对配件进行组装返回一辆完整的汽车。
应用实例
游戏角色的设计,比如有英雄,天使,恶魔等角色,都有性别,面部特征,发型,服装等属性,就可以用建造者模式来生成不同角色的游戏角色。
实例类图
实例代码
ActorDirector:
package com.zhaohy.app.actorBuilderModel;
import com.alibaba.fastjson.JSONObject;
public class ActorDirector {
public Actor construct(ActorBuilder ab) {
ab.buildType();
ab.buildSex();
ab.buildFace();
ab.buildCostume();
ab.buildHairstyle();
return ab.createActor();
}
public static void main(String[] args) {
String type = "天使";
ActorDirector director = new ActorDirector();
ActorType typeEnum = ActorType.resolve(type);
if(null == typeEnum) {
System.out.println("type不合法");
return;
}
ActorBuilder ab = null;
try {
ab = (ActorBuilder) getActorBuilder(typeEnum.getClassName());
} catch (Exception e) {
e.printStackTrace();
}
Actor actor = director.construct(ab);
System.out.println(JSONObject.toJSONString(actor));
}
private static Object getActorBuilder(String className) throws Exception {
Class<?> c = Class.forName(className);
return c.newInstance();
}
}
ActorBuilder:
package com.zhaohy.app.actorBuilderModel;
public abstract class ActorBuilder {
protected Actor actor = new Actor();
public abstract void buildType();
public abstract void buildSex();
public abstract void buildFace();
public abstract void buildCostume();
public abstract void buildHairstyle();
public Actor createActor() {
return actor;
}
}
Actor:
package com.zhaohy.app.actorBuilderModel;
public class Actor {
private String type;//角色类型
private String sex;
private String face;//脸型
private String costume;//服装
private String hairstyle; //发型
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getFace() {
return face;
}
public void setFace(String face) {
this.face = face;
}
public String getCostume() {
return costume;
}
public void setCostume(String costume) {
this.costume = costume;
}
public String getHairstyle() {
return hairstyle;
}
public void setHairstyle(String hairstyle) {
this.hairstyle = hairstyle;
}
}
AngelBuilder:
package com.zhaohy.app.actorBuilderModel;
public class AngelBuilder extends ActorBuilder {
@Override
public void buildType() {
actor.setType("天使");
}
@Override
public void buildSex() {
actor.setSex("女");
}
@Override
public void buildFace() {
actor.setFace("漂亮");
}
@Override
public void buildCostume() {
actor.setCostume("白裙");
}
@Override
public void buildHairstyle() {
actor.setHairstyle("披肩长发");
}
}
DevilBuilder:
package com.zhaohy.app.actorBuilderModel;
public class DevilBuilder extends ActorBuilder {
@Override
public void buildType() {
actor.setType("恶魔");
}
@Override
public void buildSex() {
actor.setSex("妖");
}
@Override
public void buildFace() {
actor.setFace("丑陋");
}
@Override
public void buildCostume() {
actor.setCostume("黑衣");
}
@Override
public void buildHairstyle() {
actor.setHairstyle("光头");
}
}
HeroBuilder:
package com.zhaohy.app.actorBuilderModel;
public class HeroBuilder extends ActorBuilder {
@Override
public void buildType() {
actor.setType("英雄");
}
@Override
public void buildSex() {
actor.setSex("男");
}
@Override
public void buildFace() {
actor.setFace("英俊");
}
@Override
public void buildCostume() {
actor.setCostume("盔甲");
}
@Override
public void buildHairstyle() {
actor.setHairstyle("飘逸");
}
}
ActorType枚举类:
package com.zhaohy.app.actorBuilderModel;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
public enum ActorType {
HERO("英雄", "com.zhaohy.app.actorBuilderModel.HeroBuilder"),
ANGEL("天使", "com.zhaohy.app.actorBuilderModel.AngelBuilder"),
DEVIL("恶魔", "com.zhaohy.app.actorBuilderModel.DevilBuilder");
private String type;
private String className;
ActorType(String type, String className){
this.type = type;
this.className = className;
}
public static ActorType resolve(String type) {
if (StringUtils.isBlank(type)) {
return null;
}
for (ActorType status : values()) {
if (status.type.equals(type)) {
return status;
}
}
return null;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getClassName() {
return this.className;
}
public void setClassName(String className) {
this.className = className;
}
}
如上运行ActorDirector里的main方法 得到如下结果:
{"costume":"白裙","face":"漂亮","hairstyle":"披肩长发","sex":"女","type":"天使"}
Director指挥者类的深入讨论
1.省略Director
当只有一种Builder业务类型时,可以简化结构省略掉Director类,把Director类的构造方法放进抽象Builder类中定义。
package com.zhaohy.app.actorBuilderModel;
public abstract class ActorBuilder {
protected Actor actor = new Actor();
public abstract void buildType();
public abstract void buildSex();
public abstract void buildFace();
public abstract void buildCostume();
public abstract void buildHairstyle();
public Actor createActor() {
return actor;
}
public Actor construct() {
buildType();
buildSex();
buildFace();
buildCostume();
buildHairstyle();
return actor;
}
}
这样main方法就可以改成:
public static void main(String[] args) {
String type = "天使";
// ActorDirector director = new ActorDirector();
ActorType typeEnum = ActorType.resolve(type);
if(null == typeEnum) {
System.out.println("type不合法");
return;
}
ActorBuilder ab = null;
try {
ab = (ActorBuilder) getActorBuilder(typeEnum.getClassName());
} catch (Exception e) {
e.printStackTrace();
}
// Actor actor = director.construct(ab);
Actor actor = ab.construct();
System.out.println(JSONObject.toJSONString(actor));
}
2.钩子方法的引入
建造者模式除了可以逐步构建一个复杂产品对象外,还可以通过Director类更加精细控制产品的创建过程,例如增加一类称为钩子方法的特殊方法来控制是否对某个buildPartX()进行调用。
钩子方法的返回一般是boolean类型,方法名一般为isXXX(),钩子方法定义在抽象建造者类中。
例如isBareheaded()用于判断是否为光头,在ActionBuilder提供一个默认实现,返回值为false:
package com.zhaohy.app.actorBuilderModel;
public abstract class ActorBuilder {
protected Actor actor = new Actor();
public abstract void buildType();
public abstract void buildSex();
public abstract void buildFace();
public abstract void buildCostume();
public abstract void buildHairstyle();
public Actor createActor() {
return actor;
}
public Actor construct() {
buildType();
buildSex();
buildFace();
buildCostume();
buildHairstyle();
return actor;
}
//钩子方法
public boolean isBareHeaded() {
return false;
}
}
Devil覆盖isBareheaded()方法返回true:
package com.zhaohy.app.actorBuilderModel;
public class DevilBuilder extends ActorBuilder {
@Override
public void buildType() {
actor.setType("恶魔");
}
@Override
public void buildSex() {
actor.setSex("妖");
}
@Override
public void buildFace() {
actor.setFace("丑陋");
}
@Override
public void buildCostume() {
actor.setCostume("黑衣");
}
@Override
public void buildHairstyle() {
actor.setHairstyle("光头");
}
//覆盖钩子方法
public boolean isBareHeaded() {
return true;
}
}
指挥者类代码修改如下:
public class ActorDirector {
public Actor construct(ActorBuilder ab) {
ab.buildType();
ab.buildSex();
ab.buildFace();
ab.buildCostume();
if(!ab.isBareHeaded()) {
ab.buildHairstyle();
}
return ab.createActor();
}
}
可见,通过引入钩子方法,可以在Director中对复杂产品的构建进行精细的控制,不仅指定buildPartX()方法的执行顺序,还可以控制是否需要执行某个buildPartX()方法。
建造者模式优点
1.在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
2.每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。由于指挥者类针对抽象建造者编程,增加新的具体建造者无须修改原有类库的代码,系统扩展 方便,符合开闭原则。
3.可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
建造者模式缺点
1.建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,例如很多组成部分都不相同,不适合使用建造者模式,因此其使用范围受到一定的限制。
2.如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本。
建造者模式适用环境
1.需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员变量。
2.需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
3.对象的创建过程独立于创建该对象的类。在建造者模式中通过引入指挥者类将创建过程封装在指挥者类中,而不在建造者类和客户类中。
4.隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。