设计模式之建造者模式
建造者
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
特征:用户只需指定需要建造的类型就可以得到他们,建造的过程和细节不需要知道
类型:创建型
适用场景:
- 如果一个对象有非常复杂的内部结构(很多属性)
- 想把复杂对象的创建和使用分离
优点:封装性好,创建和使用分离;扩展性好、建造类之间独立
缺点:
-
产生多余的Builder对象
-
产品内部发生改变,建造者都要修改,成本较大
UML类图:
抽象建造者(Builder)角色:给 出一个抽象接口,以规范产品对象的各个组成成分的建造。一般而言,此接口独立于应用程序的商业逻辑。模式中直接创建产品对象的是具体建造者 (ConcreteBuilder)角色。具体建造者类必须实现这个接口所要求的两种方法:一种是建造方法(buildPart1和 buildPart2),另一种是返还结构方法(retrieveResult)。一般来说,产品所包含的零件数目与建造方法的数目相符。换言之,有多少 零件,就有多少相应的建造方法。
具体建造者(ConcreteBuilder)角色:担任这个角色的是与应用程序紧密相关的一些类,它们在应用程序调用下创建产品的实例。这个角色要完成的任务包括:1.实现抽象建造者Builder所声明的接口,给出一步一步地完成创建产品实例的操作。2.在建造过程完成后,提供产品的实例。
指导者(Director)角色:担任这个角色的类调用具体建造者角色以创建产品对象。应当指出的是,指导者角色并没有产品类的具体知识,真正拥有产品类的具体知识的是具体建造者角色。
产品(Product)角色:产品便是建造中的复杂对象。一般来说,一个系统中会有多于一个的产品类,而且这些产品类并不一定有共同的接口,而完全可以是不相关联的
指导者角色是与客户端打交道的角色。指导者将客户端创建产品的请求划分为对各个零件的建造请求,再将这些请求委派给具体建造者角色。具体建造者角色是做具体建造工作的,但是却不为客户端所知。
一般来说,每有一个产品类,就有一个相应的具体建造者类。这些产品应当有一样数目的零件,而每有一个零件就相应地在所有的建造者角色里有一个建造方法
类图代码:
- 产品类Product
public class Product {
/**
* 定义一些关于产品的操作
*/
private String part1;
private String part2;
public String getPart1() {
return part1;
}
public void setPart1(String part1) {
this.part1 = part1;
}
public String getPart2() {
return part2;
}
public void setPart2(String part2) {
this.part2 = part2;
}
}
- 抽象建造者类Builder
public interface Builder {
public void buildPart1();
public void buildPart2();
public Product retrieveResult();
}
- 具体建造者类ConcreteBuilder
public class ConcreteBuilder implements Builder {
private Product product = new Product();
/**
* 产品零件建造方法1
*/
@Override
public void buildPart1() {
//构建产品的第一个零件
product.setPart1("编号:9527");
}
/**
* 产品零件建造方法2
*/
@Override
public void buildPart2() {
//构建产品的第二个零件
product.setPart2("名称:XXX");
}
/**
* 产品返还方法
*/
@Override
public Product retrieveResult() {
return product;
}
}
- 指导者类Director
public class Director {
/**
* 持有当前需要使用的建造器对象
*/
private Builder builder;
/**
* 构造方法,传入建造器对象
* @param builder 建造器对象
*/
public Director(Builder builder){
this.builder = builder;
}
/**
* 产品构造方法,负责调用各个零件建造方法
*/
public void construct(){
builder.buildPart1();
builder.buildPart2();
}
}
- 客户端类Client
public class Client {
public static void main(String[]args){
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
director.construct();
Product product = builder.retrieveResult();
System.out.println(product.getPart1());
System.out.println(product.getPart2());
}
}
客户端负责创建指导者和具体建造者对象。然后,客户端把具体建造者对象交给指导者,指导者操作具体建造者,开始创建产品。当产品完成后,建造者把产品返还给客户端。
把创建具体建造者对象的任务交给客户端而不是指导者对象,是为了将指导者对象与具体建造者对象的耦合变成动态的,从而使指导者对象可以操纵数个具体建造者对象中的任何一个。
从上可以看到,这是对多种产品的制造采用了建造者模式,进行了定义了抽象建造者接口,如果产品只有一种,那么完全可以简化整个模式,下面的具体示例中会给出代码和UML图
示例
建造者模式可以用于描述KFC如何创建套餐:套餐是一个复杂对象,它一般包含主食(如汉堡、鸡肉卷等)和饮料(如果汁、可乐等)等组成部分,不同的套餐有不同的组成部分,而KFC的服务员可以根据顾客的要求,一步一步装配这些组成部分,构造一份完整的套餐,然后返回给顾客。
- 具体的产品对象
/**
* 具体的产品对象
*/
public class Meal {
private String food;
private String drink;
public String getFood() {
return food;
}
public void setFood(String food) {
this.food = food;
}
public String getDrink() {
return drink;
}
public void setDrink(String drink) {
this.drink = drink;
}
}
- 抽象建造者,创建一个Product对象的各个部件指定的抽象接口
/**
* 抽象建造者对象
*/
public abstract class MealBuilder {
Meal meal = new Meal();
public abstract void buildFood();
public abstract void buildDrink();
public Meal getMeal(){
return meal;
}
}
- 具体建造者,构件和装配各个部件
/**
* 套餐A的建造者
*/
public class MealBuilderA extends MealBuilder {
public void buildDrink() {
meal.setDrink("可乐");
}
public void buildFood() {
meal.setFood("薯条");
}
}
/**
* 套餐B的建造者
*/
public class MealBuilderB extends MealBuilder {
public void buildDrink() {
meal.setDrink("柠檬果汁");
}
public void buildFood() {
meal.setFood("鸡翅");
}
}
- 指导者,构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象,它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
/**
* Director(指导者)
*/
public class KFCWaiter {
private MealBuilder mealBuilder;
public KFCWaiter(MealBuilder mealBuilder) {
this.mealBuilder = mealBuilder;
}
public Meal construct(){
//准备食物
mealBuilder.buildFood();
//准备饮料
mealBuilder.buildDrink();
//准备完毕,返回一个完整的套餐给客户
return mealBuilder.getMeal();
}
}
- 客户端
/**
* 建造者模式
*/
public class Client {
public static void main(String[] args) {
//套餐A
MealBuilder mealBuilderA = new MealBuilderA(); //准备套餐A的服务员
KFCWaiter waiter = new KFCWaiter(mealBuilderA);
//获得套餐
Meal mealA = waiter.construct();
System.out.print("套餐A的组成部分:");
System.out.println("食物:"+mealA.getFood()+"; "+"饮品:"+mealA.getDrink());
//套餐B
MealBuilder mealBuilderB = new MealBuilderB(); //准备套餐B的服务员
waiter = new KFCWaiter(mealBuilderB);
//获得套餐
Meal mealB = waiter.construct();
System.out.print("套餐B的组成部分:");
System.out.println("食物:"+mealB.getFood()+"; "+"饮品:"+mealB.getDrink());
}
}
套餐A的组成部分:食物:薯条; 饮品:可乐
套餐B的组成部分:食物:鸡翅; 饮品:柠檬果汁
上面说到,如果我们的产品只有一种,比如,KFC餐厅的只有薯条和可乐这样一种套餐,那么我们完全可以简化建造者模式,由于产品种类固定一种,那么就无需提供抽象建造者接口,直接提供一个具体的建造者就行,其次,对于创建一个复杂的对象,可能会有很多种不同的选择和步骤,干脆去掉“导演者”,把导演者的功能和Client的功能合并起来,也就是说,Client这个时候就相当于指导者,它来指导建造者类去构建需要的复杂对象
- 将产品和建造者合并在一起
public class Meal {
private String food;
private String drink;
//私有化构造,不允许客户端直接new
private Meal(MealBuilder mealBuilder){
this.food = mealBuilder.food;
this.drink = mealBuilder.drink;
}
public static class MealBuilder{
private String food; //这里可以设置一些默认值
private String drink; //这里可以设置一些默认值
public MealBuilder buildFood(){
this.food = "薯条";
return this;
}
public MealBuilder buildDrink(){
this.drink = "可乐";
return this;
}
public Meal getMeal(){
return new Meal(this);
}
}
@Override
public String toString() {
return "Meal{" +
"food='" + food + '\'' +
", drink='" + drink + '\'' +
'}';
}
}
- 客户端不依赖指导者,直接通过产品中的建造器创建产品实例
/**
* 简化版的建造者模式
*/
public class Client {
public static void main(String[] args) {
Meal meal = new Meal.MealBuilder().buildFood().buildDrink().getMeal();
System.out.println(meal);
}
}
Meal
相关设计模式:
- 建造者模式注重于方法的调用顺序,而工厂模式注重于创建产品,建造者模式创建一些复杂的产品,由复杂的构建组成
- 工厂模式主要是关心的是什么产品由什么工厂生产,而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
使用典范
- org.apache.http.impl.client.HttpClients
- java.lang.StringBuilder
- com.google.common.cache.CacheBuilder
- com.google.common.collect.ImmutableSet