设计模式之建造者模式

建造者

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

特征:用户只需指定需要建造的类型就可以得到他们,建造的过程和细节不需要知道

类型:创建型

适用场景

  • 如果一个对象有非常复杂的内部结构(很多属性)
  • 想把复杂对象的创建和使用分离

优点:封装性好,创建和使用分离;扩展性好、建造类之间独立

缺点

  • 产生多余的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

参考

posted @ 2020-04-19 19:40  didi516  阅读(218)  评论(0编辑  收藏  举报