建造者模式也叫生成器模式,23种设计模式之一,属于创建类模式。
在描述建造者模式之前,大家首先要明白一个道理就是任何设计模式的产生都是为了解决某一类问题,更准确的说是为了应对某一种变化,设计模式就是封装变化,把变化的部分和不变的部分隔离开。设计模式的好处就是运用面向对象的思想让我们能够更简单方便的应对外界需求的变化。比如工厂模式的产生是由于我们要根据外界的需求生产出不同类型的产品,这一变化就是外界的需求是不确定的,工厂模式就是为了应对这种变化而产生。那建造者模式是为了应对哪一种变化呢?
首先还是看下GOF对建造者模式的定义:将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建出不同的表示。GOF的语言一般都比较言简意赅,但也有点晦涩难读。
看到有网友用KFC的例子来描述建造者模式,我觉得还是比较贴近的,我们也借用这个例子描述一下:
假设KFC推出两种套餐:奥尔良鸡腿堡套餐和香辣鸡腿堡套餐,奥尔良套餐包括:一个奥尔良鸡腿堡、一个炸鸡翅、一杯雪碧。鸡腿堡套餐包括:一个香辣鸡腿堡、一份薯条、一杯可乐。每份套餐都是:主食、副食、饮料。
KFC服务员要根据顾客的要求来提供套餐,那这个需求里面什么是固定的,什么是变化的呢?很明显顾客都是要的套餐,顾客的目的是一样的。 套餐里面都是主食、副食、饮料,这也是固定的。至于主食是什么、副食是什么、饮料是什么这个是变化的。
在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成,由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将他们组合在一起的算法却相对稳定。当遇到这种场景时,你就可以考虑能否用建造者模式实现了。
下面我们用一个例子描述下建造者模式的代码实现过程。
1、第一个文件House.java
点击(此处)折叠或打开
package com.myjava.interview.enchiridion.designpattern.builderpattern.model;
public class House {
private String door;
private String floor;
private String wall;
private String windows;
public String getDoor() {
return door;
}
public void setDoor(String door) {
this.door = door;
}
public String getFloor() {
return floor;
}
public void setFloor(String floor) {
this.floor = floor;
}
public String getWall() {
return wall;
}
public void setWall(String wall) {
this.wall = wall;
}
public String getWindows() {
return windows;
}
public void setWindows(String windows) {
this.windows = windows;
}
}
2、第二个文件ChinaStyleHouse.java
点击(此处)折叠或打开
package com.myjava.interview.enchiridion.designpattern.builderpattern.model;
public class ChinaStyleHouse extends House {
//do something here
}
3、第三个文件BritishStyleHouse.java
点击(此处)折叠或打开
package com.myjava.interview.enchiridion.designpattern.builderpattern.model;
public class BritishStyleHouse extends House {
//do something here
}
4、第四个文件IHouseBuilder.java
点击(此处)折叠或打开
package com.myjava.interview.enchiridion.designpattern.builderpattern.service;
import com.myjava.interview.enchiridion.designpattern.builderpattern.model.House;
public interface IHouseBuilder {
/**
* 建造墙壁
*/
public void buildWall();
/**
* 建造房门
*/
public void buildDoor();
/**
* 建造地板
*/
public void buildFloor();
/**
* 建造窗户
* @return
*/
public void buildWindows();
/**
* 返回创造完成的对象
* @return
*/
public House getHouse();
}
5、第五个文件ChinaStyleHouseBuilderImpl.java
点击(此处)折叠或打开
import com.myjava.interview.enchiridion.designpattern.builderpattern.model.ChinaStyleHouse;
import com.myjava.interview.enchiridion.designpattern.builderpattern.model.House;
import com.myjava.interview.enchiridion.designpattern.builderpattern.service.IHouseBuilder;
public class ChinaStyleHouseBuilderImpl implements IHouseBuilder{
public House house = new ChinaStyleHouse();
@Override
public void buildDoor() {
house.setDoor("中式风格房门");
}
@Override
public void buildFloor() {
house.setFloor("中式风格地板");
}
@Override
public void buildWall() {
house.setWall("中式风格墙壁");
}
@Override
public void buildWindows() {
house.setWindows("中式风格门窗");
}
@Override
public House getHouse() {
return house;
}
}
6、第六个文件BritishStyleHouseBuilderImpl.java
点击(此处)折叠或打开
import com.myjava.interview.enchiridion.designpattern.builderpattern.model.BritishStyleHouse;
import com.myjava.interview.enchiridion.designpattern.builderpattern.model.House;
import com.myjava.interview.enchiridion.designpattern.builderpattern.service.IHouseBuilder;
public class BritishStyleHouseBuilderImpl implements IHouseBuilder {
public House house = new BritishStyleHouse();
@Override
public void buildDoor() {
house.setDoor("英式风格门");
}
@Override
public void buildFloor() {
house.setFloor("英式风格地板");
}
@Override
public void buildWall() {
house.setWall("英式风格地板");
}
@Override
public void buildWindows() {
house.setWindows("英式风格窗户");
}
@Override
public House getHouse() {
return house;
}
}
7、第七个文件HouseDirector.java
点击(此处)折叠或打开
package com.myjava.interview.enchiridion.designpattern.builderpattern.director;
import com.myjava.interview.enchiridion.designpattern.builderpattern.model.House;
import com.myjava.interview.enchiridion.designpattern.builderpattern.service.IHouseBuilder;
public class HouseDirector {
public House buildHouse(IHouseBuilder builder){
/**
* 建造房门
*/
builder.buildDoor();
/**
* 建造地板
*/
builder.buildFloor();
/**
* 建造墙壁
*/
builder.buildWall();
/**
* 建造窗户
*/
builder.buildWindows();
return builder.getHouse();
}
}
8、第八个文件TestMain.java
点击(此处)折叠或打开
import com.myjava.interview.enchiridion.designpattern.builderpattern.director.HouseDirector;
import com.myjava.interview.enchiridion.designpattern.builderpattern.model.House;
import com.myjava.interview.enchiridion.designpattern.builderpattern.service.IHouseBuilder;
import com.myjava.interview.enchiridion.designpattern.builderpattern.service.impl.BritishStyleHouseBuilderImpl;
import com.myjava.interview.enchiridion.designpattern.builderpattern.service.impl.extend.ChinaStyleAHouseBuilderImpl;
public class TestClient {
public static void main(String [] args){
IHouseBuilder cbuilder = new ChinaStyleHouseBuilderImpl();
House chouse = new HouseDirector().buildHouse(cbuilder);
System.out.println("------------------------------");
System.out.println("房门属性:" + chouse.getDoor());
System.out.println("墙壁属性:" + chouse.getWall());
System.out.println("天花板属性:" + chouse.getFloor());
System.out.println("窗户属性:" + chouse.getWindows());
System.out.println("------------------------------");
IHouseBuilder ebuilder = new BritishStyleHouseBuilderImpl();
House ehouse = new HouseDirector().buildHouse(ebuilder);
System.out.println("房门属性:" + ehouse.getDoor());
System.out.println("墙壁属性:" + ehouse.getWall());
System.out.println("天花板属性:" + ehouse.getFloor());
System.out.println("窗户属性:" + ehouse.getWindows());
System.out.println("------------------------------");
}
}
如上功能代码中House对象就如同KFC例子中的套餐,它是一个抽象的概念,而ChinaStyleHouse对象和BritishStyleHouse对象是具体的两种产品如同KFC例子中的奥尔良套餐和鸡腿堡套餐。
IHouseBuilder是一个抽象的建造者对象,而ChinaStyleHouseBuilderImpl和BritishStyleHouseBuilderImpl就是两种产品的实际建造者,它们对应了KFC例子中制造两种套餐的工人,这部分功能的代码相对来说是经常变化的,而HouseDiretor中的逻辑相对来说是固定不变的。
9、建造者模式的优点
(1)使用建造者模式可以使客户端不必知道产品内部组成的细节。
(2)具体的建造者类之间是相互独立的,对系统的扩展非常有利。
(3)由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。