问题背景
你要建造一所房子。大兴土木当然应该用很多劳力,但是知识分子也是必要的——要不房子结构会出问题,或者很难看。你需要一个设计师,这个人呢,知道怎么建房子。这个人会告诉你,建房子啊,应该先从底下开始(神说地基已经打好了),你得先弄弄地板,再弄弄墙,再在墙上打几个洞,一些当窗户,弄个接地气的当门。
设计一
所以我们有了一个无脑的设计。
class House {
private void buildFloor() {
//
}
private void buildWall() {
//
}
private void buildWindows() {
//
}
private void buildDoor() {
//
}
public House() {
buildFloor();
buildWall();
buildWindows();
buildDoor();
}
}
不过其实也不是非常无脑,起码你没把四个 build*
函数的东西一股脑都填到构造函数里。
设计一的问题
那么这个设计怎么样呢?直接看起来好像还好。
可是天要亡你。用户改需求啦!用户说,我有 100 套房子(壕),怎么能都建成一样的呢?我要这几套建成中式(China
)的,这几套建成罗马式(Rome
)的,那几套……
设计师一听就头大了,这样一想原来的设计的确有问题:它的扩展性太差。不能支持“房子”的多样性。
出现这种状况的原因是什么呢?我们看一看这个类有什么功能。第一、它建立了房子的各个部分;第二、它将各个部分组合起来。
如果是一个简单一点的类的话,这样写其实没有多大问题,类有几个部分的话自然就应该这么构造。可是房子这个类比较复杂,它的种类太多也就是第一个功能部分要求扩展性;同时又有比较固定的构成,有了地板、墙壁、门等的构造方法之后,不论他们是什么风格的,组合起来的逻辑都是一样的。
也就是说,房子这个类具有两个特性:
- 成分多样性高。
- 各个成分直接组成逻辑固定。
对应着两项特性,构造房子这个工作,是由两个特征鲜明的部分组成:构建各个成分和设计组装。在一个类中完成两项工作是不合适的,所以我们将把两个部分分开来做。
Builder 模式
首先我们来弄一个 Builder
类,用这个类做构建各个部分这件事情。
package builder;
import house.House;
public class Builder {
House house = new House();
public void buildFloor();
public void buildWall();
public void buildWindows();
public void buildDoor();
public House getHouse();
}
类里的 getHouse(): void
就是得到最终房子的方法。请注意,这个 Builer
只是“民工”,它不懂设计,只知道每一部分怎么弄。
“民工”需要一个设计师来指挥,设计师就是完成第二件事,设计组装的人。
package designer;
import builder.Builder;
/**
* @author plus7wist
*
*/
public class Designer {
public void concreteHouse(Builder builder) {
builder.buildFloor();
builder.buildWall();
builder.buildDoor();
builder.buildWindows();
}
}
代码里很明显可以看出,设计师其实什么都不干,它只是指挥 builder
进行工作,它的指挥体现出了设计的逻辑,而这一点 builder
又是不必操心的。
这种两者合作的结构就是典型的 Builder 模式。
扩展性
我们再看看这个设计进行扩展的时候会怎么样。无论风格怎么改变,Designer
的逻辑都不变。对于多样变化的 Builder
我们很容易想到一种解决办法:利用多态,将原来的 Builder
变成抽象的。
package builder;
import house.House;
public abstract class Builder {
public abstract void buildFloor();
public abstract void buildWall();
public abstract void buildWindows();
public abstract void buildDoor();
public abstract House getHouse();
}
为了表示方便我们简要的写一下 House
。用一个数组把成分的样子存起来。
package house;
import java.util.ArrayList;
public class House {
private ArrayList<String> parts = new ArrayList<String>();
public void add(String part) {
parts.add(part);
}
public void display() {
for (int i = 0; i < parts.size(); ++i)
System.out.print((i == 0 ? "" : ",") + parts.get(i));
System.out.println();
}
}
然后如果建中国风格的房子的话,就建一个ChinaHouseBuilder
,继承自 Builder
。
package concrete.builder;
import house.House;
import builder.Builder;
/**
* @author plus7wist
*
*/
public class ChinaHouseBuilder extends Builder {
House chinaHouse = new House();
@Override
public void buildFloor() {
chinaHouse.add("China Floor");
}
@Override
public void buildWall() {
chinaHouse.add("China Wall");
}
@Override
public void buildWindows() {
chinaHouse.add("China Windows");
}
@Override
public void buildDoor() {
chinaHouse.add("China Door");
}
@Override
public House getHouse() {
return chinaHouse;
}
}
如果建罗马风格的房子的话,就建一个RomeHouseBuilder
。
package concrete.builder;
import house.House;
import builder.Builder;
/**
* @author plus7wist
*
*/
public class RomeHouseBuilder extends Builder {
House romeHouse = new House();
@Override
public void buildFloor() {
romeHouse.add("Rome Floor");
}
@Override
public void buildWall() {
romeHouse.add("Rome Wall");
}
@Override
public void buildWindows() {
romeHouse.add("Rome Windows");
}
@Override
public void buildDoor() {
romeHouse.add("Rome Door");
}
@Override
public House getHouse() {
return romeHouse;
}
}
如此一来,我们发现 Designer
的逻辑完全不用改——当真是个好的设计。
最后我们看一眼怎么使用这个东西。
package main;
import builder.Builder;
import house.House;
import concrete.builder.*;
import designer.Designer;
/**
* @author plus7wist
*
*/
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
Builder chinaHouseBuilder = new ChinaHouseBuilder();
Builder romeHouseBuilder = new RomeHouseBuilder();
Designer designer = new Designer();
designer.concreteHouse(chinaHouseBuilder);
designer.concreteHouse(romeHouseBuilder);
House chinaHouse = chinaHouseBuilder.getHouse();
House romeHouse = romeHouseBuilder.getHouse();
chinaHouse.display();
romeHouse.display();
}
}