创建类型5-4:建造者模式(Builder Pattern)

1. 概述

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

一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的,它提供了一种创建对象的最佳方式。

2. 介绍

2.1 意图

将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

2.2 主要解决

主要解决在软件系统中,有时候面临着" 一个复杂对象 "的创建工作,其通常由各个部分的子对象用一定的算法构成;
由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

2.3 何时使用

一些基本部件不会变,而其组合经常变化的时候。

2.4 如何解决

将变与不变分离开。

2.5 关键代码

建造者:创建和提供实例。
导演:管理建造出来的实例的依赖关系。

2.6 应用实例

1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。
2、JAVA 中的 StringBuilder。

2.7 优点

1、建造者独立,易扩展。
2、便于控制细节风险。

2.8 缺点

1、产品必须有共同点,范围有限制。
2、如内部变化复杂,会有很多的建造类。

2.9 使用场景

1、需要生成的对象具有复杂的内部结构。
2、需要生成的对象内部属性本身相互依赖。

2.10 注意事项

与工厂模式的区别是:建造者模式更加关注与零件装配的顺序和组合。

建造者模式与抽象工厂模式有点相似,但是建造者模式返回一个完整的复杂产品,而抽象工厂模式返回一系列相关的产品
在抽象工厂模式中,客户端通过选择具体工厂来生成所需对象,而在建造者模式中,客户端通过指定具体建造者类型并指导Director类如何去生成对象,侧重于一步步构造一个复杂对象,然后将结果返回。

在建造者模式的结构中还引入了一个指挥者类Director,该类主要有两个作用:一方面它隔离了客户与创建过程;另一方面它控制产品的创建过程,客户只能访问Director内提供的方法属性。而抽象方法,则未隔离创建过程,通过返回原始产品对象,让使用者直接访问产品对象的方法属性

3. 参与者

1.Product
	表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程。
	包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
	
2.Builder
	创建一个Product对象的各个部件指定抽象接口。
	实现Builder的接口以构造和装配该产品的各个部件。
	定义并明确它所创建的表示。
	提供一个检索产品的接口。

3.Director
	构造一个使用Builder接口的对象。

4. 类图

在这里插入图片描述

5. 例子

5.1 Product

public class Person {
    private String head;
    private String body;
    private String foot;

    public String getHead() {
        return head;
    }

    public void setHead(String head) {
        this.head = head;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

    public String getFoot() {
        return foot;
    }

    public void setFoot(String foot) {
        this.foot = foot;
    }
}

5.2 Builder

public interface PersonBuilder {
    void buildHead();
    void buildBody();
    void buildFoot();

    Person buildPerson();
}

class Man extends Person {}

class ManBuilder implements PersonBuilder {
    Person person;
    
    public ManBuilder() {
        person = new Man();
    }
    
    @override
    public void buildBody() {
        person.setBody("建造男人的身体");
    }
    
		@override
    public void buildFoot() {
        person.setFoot("建造男人的脚");
    }
    
		@override
    public void buildHead() {
        person.setHead("建造男人的头");
    }
    
		@override
    public Person buildPerson() {
        return person;
    }
}

5.3 Director

public class PersonDirector {
    public Person constructPerson(PersonBuilder pb) {
        pb.buildHead();
        pb.buildBody();
        pb.buildFoot();
        return pb.buildPerson();
    }
}

Test

public class Test{
    public static void main(String[] args) {
        PersonDirector pd = new PersonDirector();
        Person person = pd.constructPerson(new ManBuilder());
        System.out.println(person.getBody());
        System.out.println(person.getFoot());
        System.out.println(person.getHead());
    }
}

result

建造男人的身体
建造男人的脚
建造男人的头

6. 示例2

我们假设一个快餐店的商业案例,其中,一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒中。冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子中。

我们将创建一个表示食物条目(比如汉堡和冷饮)的 Item 接口和实现 Item 接口的实体类,以及一个表示食物包装的 Packing 接口和实现 Packing 接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。

然后我们创建一个 Meal 类,带有 ItemArrayList 和一个通过结合 Item 来创建不同类型的 Meal 对象的 MealBuilderBuilderPatternDemo,我们的演示类使用 MealBuilder 来创建一个 Meal
在这里插入图片描述

6.1 Product

public interface Packing {
    public String pack();
}

class Wrapper implements Packing {
    @Override
    public String pack() {
        return "Wrapper";
    }
}

class Bottle implements Packing {
    @Override
    public String pack() {
        return "Bottle";
    }
}
interface Item {
   public String name();
   public Packing packing();
   public float price();    
}
public abstract class Burger implements Item {
    @Override
    public Packing packing() {
        return new Wrapper();
    }
    @Override
    public abstract float price();
}

class VegBurger extends Burger {
    @Override
    public float price() {
        return 25.0f;
    }
    @Override
    public String name() {
        return null;
    }
}

class ChickenBurger extends Burger {
    @Override
    public float price() {
        return 50.5f;
    }
    @Override
    public String name() {
        return "Chicken Burger";
    }
}
public abstract class ColdDrink implements Item {
    @Override
    public Packing packing() {
        return new Bottle();
    }
    @Override
    public abstract float price();
}

class Coke extends ColdDrink {
    @Override
    public float price() {
        return 30.0f;
    }
    @Override
    public String name() {
        return "Coke";
    }
}

class Pepsi extends ColdDrink {
    @Override
    public float price() {
        return 35.0f;
    }
    @Override
    public String name() {
        return "Pepsi";
    }
}

6.2 Builder

import java.util.ArrayList;
import java.util.List;

public class MealBuilder {
    private List<Item> items = new ArrayList<Item>();

    public void addItem(Item item){
        items.add(item);
    }

    public float getCost(){
        float cost = 0.0f;
        for(Item item: items){
            cost += item.price();
        }
        return cost;
    }

    public void showItems(){
        for(Item item: items){
            System.out.print("Item : "+item.name());
            System.out.print(", Packing : "+item.packing().pack());
            System.out.println(", Price : "+item.price());
        }
    }
}

6.3 Director

public class MealDirector {
    public MealBuilder prepareVegMeal(){
        MealBuilder mealBuilder = new MealBuilder();
        mealBuilder.addItem(new VegBurger());
        mealBuilder.addItem(new Coke());
        return mealBuilder;
    }

    public MealBuilder prepareNonVegMeal(){
        MealBuilder mealBuilder = new MealBuilder();
        mealBuilder.addItem(new ChickenBurger());
        mealBuilder.addItem(new Pepsi());
        return mealBuilder;
    }
}

test

public class BuilderPatternDemo {
    public static void main(String[] args) {
        MealBuilder mealBuilder = new MealDirector();

        Meal vegMeal = mealDirector.prepareVegMeal();
        System.out.println("Veg Meal");
        vegMeal.showItems();
        System.out.println("Total Cost: " +vegMeal.getCost());

        Meal nonVegMeal = mealDirector.prepareNonVegMeal();
        System.out.println("\n\nNon-Veg Meal");
        nonVegMeal.showItems();
        System.out.println("Total Cost: " +nonVegMeal.getCost());
    }
}
Veg Meal
Item : null, Packing : Wrapper, Price : 25.0
Item : Coke, Packing : Bottle, Price : 30.0
Total Cost: 55.0

Non-Veg Meal
Item : Chicken Burger, Packing : Wrapper, Price : 50.5
Item : Pepsi, Packing : Bottle, Price : 35.0
Total Cost: 85.5
posted @ 2019-05-21 18:50  南山道士  阅读(65)  评论(0编辑  收藏  举报