建造者模式与StringBuilder

429 阅读5分钟

1. 建造者模式(Builder Pattern)

1.1. 介绍

建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。

类型:属于创建型模式(创建型、结构型、行为型)

意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。

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

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

如何解决:将变与不变分离开。

关键代码:建造者:创建和提供实例,指挥者:管理建造出来的实例的依赖关系。

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

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

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

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

注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。而工厂方法模式更注重零部件的创建过程,但两者可以结合使用。工厂模式关注创建单个产品,建造者模式关注的是组成产品的各个部分。

1.2. 模式的结构与实现

1.2.1 模式的结构

建造者(Builder)模式的主要角色如下。 产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个部件。 抽象建造者(Builder):它包含创建产品各个子部件的抽象方法,通常还包含一个返回复杂产品的方法。 具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息builder-pattern

1.2.2 模式的实现

(1) 产品角色:包含多个组成部件的复杂对象。 class ProductDemo { private String partA; private String partB; private String partC; }

(2) 抽象建造者:包含创建产品各个子部件的抽象方法。

abstract class AbstractBuilderDemo { //创建产品对象 protected ProductDemo product = new ProductDemo();

public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();

//返回产品对象
public ProductDemo getResult() {
    return product;
}

}

(3) 具体建造者:实现了抽象建造者接口。 public class ConcreteBuilderDemo extends AbstractBuilderDemo { @Override public void buildPartA() { product.setPartA("PartA"); } @Override public void buildPartB() { product.setPartB("PartB"); } @Override public void buildPartC() { product.setPartC("PartC"); } }

(4) 指挥者:调用建造者中的方法完成复杂对象的创建。

class DirectorDemo { private AbstractBuilderDemo builder; public Director(AbstractBuilderDemo builder) { this.builder=builder; }

//产品构建与组装方法
public ProductDemo construct() {
    builder.buildPartA();
    builder.buildPartB();
    builder.buildPartC();
    return builder.getResult();
}

}

(5) 客户类。

public class ClientDemo { public static void main(String[] args) { Builder builder=new ConcreteBuilder(); Director director=new Director(builder); Product product=director.construct(); product.show(); } }

1.3. 模式的扩展

建造者(Builder)模式在应用过程中可以根据需要改变,抽象建造者和指挥者角色不是必须的。如果创建的产品种类只有一种,只需要一个具体建造者,这时可以省略掉抽象建造者。 StringBuilder忽略了指挥者,将指挥者身份转移给用户,字符串的组合方式无法枚举,交给用户灵活的创建。

2. StringBuilder中的建造者模式

2.1. 角色

产品角色(Product):String 抽象建造者(Builder):AbstractStringBuilder 具体建造者(Concrete Builder):StringBuilder 指挥者(Director):/

StringBuilder public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {

static final long serialVersionUID = 4383685877147921099L;

public StringBuilder() {
    super(16);
}

public StringBuilder(int capacity) {
    super(capacity);
}

public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
}

public StringBuilder(CharSequence seq) {
    this(seq.length() + 16);
    append(seq);
}

@Override
public StringBuilder append(Object obj) {
    return append(String.valueOf(obj));
}

@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

public StringBuilder append(StringBuffer sb) {
    super.append(sb);
    return this;
}

...
...

@Override
public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}

} AbstractStringBuilder abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ char[] value;

/**
 * The count is the number of characters used.
 */
int count;

/**
 * This no-arg constructor is necessary for serialization of subclasses.
 */
AbstractStringBuilder() {
}

/**
 * Creates an AbstractStringBuilder of the specified capacity.
 */
AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

/**
 * Characters are copied from this sequence into the
 * destination character array {@code dst}. The first character to
 * be copied is at index {@code srcBegin}; the last character to
 * be copied is at index {@code srcEnd-1}. The total number of
 * characters to be copied is {@code srcEnd-srcBegin}. The
 * characters are copied into the subarray of {@code dst} starting
 * at index {@code dstBegin} and ending at index:
 * <pre>{@code
 * dstbegin + (srcEnd-srcBegin) - 1
 * }</pre>
 *
 * @param      srcBegin   start copying at this offset.
 * @param      srcEnd     stop copying at this offset.
 * @param      dst        the array to copy the data into.
 * @param      dstBegin   offset into {@code dst}.
 * @throws     IndexOutOfBoundsException  if any of the following is true:
 *             <ul>
 *             <li>{@code srcBegin} is negative
 *             <li>{@code dstBegin} is negative
 *             <li>the {@code srcBegin} argument is greater than
 *             the {@code srcEnd} argument.
 *             <li>{@code srcEnd} is greater than
 *             {@code this.length()}.
 *             <li>{@code dstBegin+srcEnd-srcBegin} is greater than
 *             {@code dst.length}
 *             </ul>
 */
public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
{
    if (srcBegin < 0)
        throw new StringIndexOutOfBoundsException(srcBegin);
    if ((srcEnd < 0) || (srcEnd > count))
        throw new StringIndexOutOfBoundsException(srcEnd);
    if (srcBegin > srcEnd)
        throw new StringIndexOutOfBoundsException("srcBegin > srcEnd");
    System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}

public AbstractStringBuilder append(Object obj) { return append(String.valueOf(obj)); }

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;
}

// Documentation in subclasses because of synchro difference
public AbstractStringBuilder append(StringBuffer sb) {
    if (sb == null)
        return appendNull();
    int len = sb.length();
    ensureCapacityInternal(count + len);
    sb.getChars(0, len, value, count);
    count += len;
    return this;
}

/**
 * @since 1.8
 */
AbstractStringBuilder append(AbstractStringBuilder asb) {
    if (asb == null)
        return appendNull();
    int len = asb.length();
    ensureCapacityInternal(count + len);
    asb.getChars(0, len, value, count);
    count += len;
    return this;
}

// Documentation in subclasses because of synchro difference
@Override
public AbstractStringBuilder append(CharSequence s) {
    if (s == null)
        return appendNull();
    if (s instanceof String)
        return this.append((String)s);
    if (s instanceof AbstractStringBuilder)
        return this.append((AbstractStringBuilder)s);

    return this.append(s, 0, s.length());
}

private AbstractStringBuilder appendNull() {
    int c = count;
    ensureCapacityInternal(c + 4);
    final char[] value = this.value;
    value[c++] = 'n';
    value[c++] = 'u';
    value[c++] = 'l';
    value[c++] = 'l';
    count = c;
    return this;
}

@Override
public AbstractStringBuilder append(CharSequence s, int start, int end) {
    if (s == null)
        s = "null";
    if ((start < 0) || (start > end) || (end > s.length()))
        throw new IndexOutOfBoundsException(
            "start " + start + ", end " + end + ", s.length() "
            + s.length());
    int len = end - start;
    ensureCapacityInternal(count + len);
    for (int i = start, j = count; i < end; i++, j++)
        value[j] = s.charAt(i);
    count += len;
    return this;
}

public AbstractStringBuilder append(char[] str) {
    int len = str.length;
    ensureCapacityInternal(count + len);
    System.arraycopy(str, 0, value, count, len);
    count += len;
    return this;
}

public AbstractStringBuilder append(char str[], int offset, int len) {
    if (len > 0)                // let arraycopy report AIOOBE for len < 0
        ensureCapacityInternal(count + len);
    System.arraycopy(str, offset, value, count, len);
    count += len;
    return this;
}

public AbstractStringBuilder append(boolean b) {
    if (b) {
        ensureCapacityInternal(count + 4);
        value[count++] = 't';
        value[count++] = 'r';
        value[count++] = 'u';
        value[count++] = 'e';
    } else {
        ensureCapacityInternal(count + 5);
        value[count++] = 'f';
        value[count++] = 'a';
        value[count++] = 'l';
        value[count++] = 's';
        value[count++] = 'e';
    }
    return this;
}

@Override
public AbstractStringBuilder append(char c) {
    ensureCapacityInternal(count + 1);
    value[count++] = c;
    return this;
}

public AbstractStringBuilder append(int i) {
    if (i == Integer.MIN_VALUE) {
        append("-2147483648");
        return this;
    }
    int appendedLength = (i < 0) ? Integer.stringSize(-i) + 1
                                 : Integer.stringSize(i);
    int spaceNeeded = count + appendedLength;
    ensureCapacityInternal(spaceNeeded);
    Integer.getChars(i, spaceNeeded, value);
    count = spaceNeeded;
    return this;
}

public AbstractStringBuilder append(long l) {
    if (l == Long.MIN_VALUE) {
        append("-9223372036854775808");
        return this;
    }
    int appendedLength = (l < 0) ? Long.stringSize(-l) + 1
                                 : Long.stringSize(l);
    int spaceNeeded = count + appendedLength;
    ensureCapacityInternal(spaceNeeded);
    Long.getChars(l, spaceNeeded, value);
    count = spaceNeeded;
    return this;
}


public AbstractStringBuilder append(float f) {
    FloatingDecimal.appendTo(f,this);
    return this;
}

public AbstractStringBuilder append(double d) {
    FloatingDecimal.appendTo(d,this);
    return this;
}

/**
 * Inserts the string representation of a subarray of the {@code str}
 * array argument into this sequence. The subarray begins at the
 * specified {@code offset} and extends {@code len} {@code char}s.
 * The characters of the subarray are inserted into this sequence at
 * the position indicated by {@code index}. The length of this
 * sequence increases by {@code len} {@code char}s.
 *
 * @param      index    position at which to insert subarray.
 * @param      str       A {@code char} array.
 * @param      offset   the index of the first {@code char} in subarray to
 *             be inserted.
 * @param      len      the number of {@code char}s in the subarray to
 *             be inserted.
 * @return     This object
 * @throws     StringIndexOutOfBoundsException  if {@code index}
 *             is negative or greater than {@code length()}, or
 *             {@code offset} or {@code len} are negative, or
 *             {@code (offset+len)} is greater than
 *             {@code str.length}.
 */
public AbstractStringBuilder insert(int index, char[] str, int offset,
                                    int len)
{
    if ((index < 0) || (index > length()))
        throw new StringIndexOutOfBoundsException(index);
    if ((offset < 0) || (len < 0) || (offset > str.length - len))
        throw new StringIndexOutOfBoundsException(
            "offset " + offset + ", len " + len + ", str.length "
            + str.length);
    ensureCapacityInternal(count + len);
    System.arraycopy(value, index, value, index + len, count - index);
    System.arraycopy(str, offset, value, index, len);
    count += len;
    return this;
}

...
...

@Override
public abstract String toString();

}