建造者模式

维基百科上面的解释是:

建造模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。

应用场景

适用于当需要根据不同情形创建相同产品,但产品属性各异的情景。

实例

例如一款游戏中用户可以根据需要创建不同的游戏角色(Actor)。游戏角色暂时只有两种(后续可以扩展),人族(Human)和狼族(Wolf)。角色有三个属性,种族名(name)、职位(title)和性别(sex)。

角色类(Actor)

String name;
String title;
int sex;
void setName(String name);
void setTitle(String title);
void setSex(int sex);

抽象的角色创建类(ActorBuilder)包含角色(Actor),并设置角色的各个属性。并且提供方法返回这个角色。

void buildName();
void buildTitle();
void buildSex();
Actor createActor();

具体的创建类如人族创建类(HumanBuilder)实现或继承抽象的角色创建类(ActorBuilder)。

void buildName(){
    actor.setName("Human");
}
void buildTitle(){
    actor.setTitle("Lord");
}
void buildSex(){
    actor.setSex(1);
}
Actor createActor(){
    return actor;
}

角色创建控制器(ActorController)定义角色属性的创建次序,并返回角色给调用者(客户端)。

Actor construct(ActorBuilder builder){
    builder.buildName();
    builder.buildTitle();
    builder.buildSex();
    return builder.createActor();
}

演示代码

Actor.java

public class Actor{
    private String name;
    private String title;
    private int sex;

    public void setName(String name){
        this.name = name;
    }

    public void setTitle(String title){
        this.title = title;
    }

    public void setSex(int sex){
        this.sex = sex;
    }

    public String getName(){
        return name;
    }

    public String getTitle(){
        return title;
    }

    public int getSex(){
        return sex;
    }

    public String toString(){
        return "name:" + name + ", title:" + title + ", sex:" + (sex==0?"Female":"Male");
    }

}

ActorBuilder.java

interface ActorBuilder{
    Actor actor = new Actor();
    void buildName();
    void buildTitle();
    void buildSex();
    Actor createActor();
}

HumanBuilder.java

public class HumanBuilder implements ActorBuilder{
    public void buildName(){
        actor.setName("Human");
    }

    public void buildTitle(){
        actor.setTitle("Lord");
    }

    public void buildSex(){
        actor.setSex(1);
    }

    public Actor createActor(){
        return actor;
    }
}

WolfBuilder.java

public class WolfBuilder implements ActorBuilder{
    public void buildName(){
        actor.setName("Wolf");
    }

    public void buildTitle(){
        actor.setTitle("Ghost");
    }

    public void buildSex(){
        actor.setSex(0);
    }

    public Actor createActor(){
        return actor;
    }
}

ActorController.java

public class ActorController{

    public Actor construct(ActorBuilder builder){
        builder.buildName();
        builder.buildTitle();
        builder.buildSex();
        return builder.createActor();
    }
}

builder.properties

#className=HumanBuilder
className=WolfBuilder

PropertyUtil.java

import java.io.*;
import java.util.*;
public class PropertyUtil{
    public static Object getBean(){
        Object obj = null;
        try{
            Properties prop = new Properties();
            InputStream in = new BufferedInputStream(new FileInputStream("builder.properties"));
            prop.load(in);
            String className = prop.getProperty("className");
            Class c = Class.forName(className);
            obj = c.newInstance();
        }catch(Exception e){
            //Exception
            e.printStackTrace();
        }
        return obj;
    }
}

Client.java

public class Client{
    public static void main(String args[]){
        ActorBuilder builder = (ActorBuilder)PropertyUtil.getBean();

        ActorController controller = new ActorController();
        Actor actor = controller.construct(builder);
        System.out.print(actor.toString());
    }
}

扩展

简化后的控制类

如果简化结构,可以将角色创建控制类(ActorController)和抽象角色创建类(ActorBuilder)合并。在抽象角色创建类中指定角色属性的创建次序并返回角色。

abstract class ActorBuilder{
    Actor actor = new Actor();
    void buildName();
    void buildTitle();
    void buildSex();
    Actor construct(){
        this.buildName();
        this.buildTitle();
        this.buildSex();
        return actor;
    }
}

钩子的使用

有时我们需要更精细的控制产品的创建过程,这时可以使用一种特殊的方法(钩子方法)去控制某方法是否能够被调用(属性是否能被设置)。例如:对于狼族(Wolf)而言没有职位(tile)的说法。可以在抽象角色创建类(ActorBuilder)中加入钩子方法(boolean isOfficer())。在具体的儿狼族角色创建类(WolfBuilder)中覆盖钩子方法。

abstract class ActorBuilder{
    Actor actor = new Actor();
    void buildName();
    void buildTitle();
    void buildSex();

    boolean isOfficer(){
        return true;
    }

    Actor construct(){
        this.buildName();
        if(this.isOfficer()){
            this.buildTitle();
        }
        this.buildSex();
        return actor;
    }
}
public class WolfBuilder implements ActorBuilder{
    public void buildName(){
        actor.setName("Wolf");
    }

    public void buildTitle(){
        actor.setTitle("Ghost");
    }

    public void buildSex(){
        actor.setSex(0);
    }

    public boolean isOfficer(){
        return false;
    }
}

改进后的代码演示

  • ActorBuilder.java
abstract class ActorBuilder{
    Actor actor = new Actor();
    abstract void buildName();
    abstract void buildTitle();
    abstract void buildSex();

    boolean isOfficer(){
        return true;
    }

    Actor construct(){
        this.buildName();
        if(this.isOfficer()){
            this.buildTitle();
        }
        this.buildSex();

        return actor;
    }
}
  • HumanBuilder.java
public class HumanBuilder extends ActorBuilder{
    public void buildName(){
        actor.setName("Human");
    }

    public void buildTitle(){
        actor.setTitle("Lord");
    }

    public void buildSex(){
        actor.setSex(1);
    }
}
  • WolfBuilder.java
public class WolfBuilder extends ActorBuilder{
    public void buildName(){
        actor.setName("Wolf");
    }

    public void buildTitle(){
        actor.setTitle("Ghost");
    }

    public void buildSex(){
        actor.setSex(0);
    }

    public boolean isOfficer(){
        return false;
    }
}
  • Client.java
public class Client{
    public static void main(String args[]){
        ActorBuilder builder = (ActorBuilder)PropertyUtil.getBean();

        Actor actor = builder.construct();
        System.out.print(actor.toString());
    }
}

(完)