设计模式——建造者模式

166 阅读4分钟

概述

在现实生活或软件系统中,都存在一些由多个部分组成的复杂类,例如一个KFC套餐,它里面包括食品(汉堡,薯条,蛋挞等),饮料(可乐,雪碧等),不同套餐的食品和饮料的构成也不同。作为客户的我们无需关注这个套餐是如何生成的,我们只需要与服务员交流,表明自己需要哪种套餐,这样服务员(指挥者)就会通过 操作生成该套餐的食品,饮料的机器,然后返回套餐。

简单的的说,建造者模式是指用户只通过指定复杂对象(套餐)的类型和内容(套餐的类型和其不同组成)就可以构建它们,用户无需知道内部的具体构建细节。

在建造者模式的主要角色有:

  • 产品角色,如本例中的套餐,它包含食物对象和饮料对象。
  • 抽象建造者,本例中生成食物和饮料的机器的大致功能描述
  • 具体创建者,本例中生成不同食物和不通过饮料的机器
  • 指挥者:它包含抽象建造者实例对象。它主要负责隔离客户与对象的生产过程 和 负责控制产品对象的生产过程。如本例中的服务员。

优点

(1)用户不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。

(2)每一个具体建造者都相对独立,而与其他的具体建造者无关,用户使用不同的具体建造者即可得到不同的产品对象 。

(3)增加新的具体建造者无须修改原有类库的代码,符合 “开闭原则”。

什么情况下不要用建造者模式?

(1)产品差异性很大的情况:建造者模式所创建的产品一般具有较多的共同点,其组成部分相似。

(2)产品内部变化很复杂的情况:如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类,导致系统庞大。

工厂模式与建造者模式的区别

(1)在工厂模式下,一个工厂生产一个/一组产品,该模式下不关系构建过程,只需知道该产品由哪些工厂组成即可。它关注的是一个产品整体。

(2)在建造者模式下,一个具体产品的产生是依赖于各个部件的产生和装配顺序。它关注的是”由零件一步一步地组装出产品对象“。它关注的是产品组成部分的创建过程。


建造者模式示例

以KFC套餐为例,一个套餐分为食品和饮料,不同套餐的食品和饮料不同。

一个套餐就是一个产品角色,套餐里有食品和饮料。

public class Meal {
    private String food;
    private String drink;
    //省略get/set方法
}

抽象建造者。它包含一个产品角色,它大致描述了它有什么功能:生产食品和饮料。即生产不同套餐的机器的大致描述

public abstract class MealBuilder {
    Meal meal = new Meal();
    public abstract void buildFood();
    public abstract void buildDrink();
    public Meal getMeal(){ return meal; }
}

具体建造者,它实现了抽象建造者的三个生产功能,即生产不同套餐内容的机器。

public class MealA extends MealBuilder{
    public void buildDrink() {
        meal.setDrink("可乐");
    }

    public void buildFood() {
        meal.setFood("薯条");
    }
}
public class MealB extends MealBuilder{
    public void buildDrink() {
        meal.setDrink("柠檬果汁");
    }

    public void buildFood() {
        meal.setFood("鸡翅");
    }
}

指挥者。它包含一个抽象建造者实例builder。它通过builder来返回生产的产品,就好比一个服务员KFCWaiter操作不同机器MealBuilder 来生成产品(套餐),并返回 产品。

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 Test {
    public static void main(String[] args) {
        //套餐A
        MealA a = new MealA();
        //准备套餐A的服务员
        KFCWaiter waiter = new KFCWaiter(a);
        //获得套餐
        Meal mealA = waiter.construct();      
        System.out.print("套餐A的组成部分:");
        System.out.println("食物:"+mealA.getFood()+";   "+"饮品:"+mealA.getDrink());
    }
}

StringBuilder 中的建造者模式

下图是StringBuilder 的继承关系图

Appendable 接口方法如下,

public interface Appendable {
    Appendable append(CharSequence csq) throws IOException;
    Appendable append(CharSequence csq, int start, int end) throws IOException;
    Appendable append(char c) throws IOException;
}

StringBuilder 的append方法如下所示,它调用了父类AbstractStringBuilder的append方法。

public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
}

AbstractStringBuilder 的append方法如下:

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value;
    int count;
    
    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }
    
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }
}

因此,Appendable接口是抽象建造者,定义建造方法append。建造方法的实现由AbstractStringBuilder 类完成,它是具体建造者,它的子类StringBuilder 充当了指挥者角色。


参考资料

JavaGuide