设计模式九--建造者模式

174 阅读7分钟

这是我参与更文挑战的第22天,活动详情查看: 更文挑战

设计模式

建造者模式

比如我们建造房子,建房子的过程有打桩、砌墙、封顶,房子的类型有平房、楼房、别墅

我们的代码这样写的

AbsHouse

package com.wangscaler.builder;

/**
 * @author wangscaler
 * @date 2021.06.24 13:56
 */
public abstract class AbsHouse {
    public abstract void layingFoundation();

    public abstract void buildWall();

    public abstract void sealRoof();

    public void build() {
        layingFoundation();
        buildWall();
        sealRoof();
    }
}

Bungalow

package com.wangscaler.builder;

/**
 * @author wangscaler
 * @date 2021.06.24 13:56
 */
public class Bungalow extends AbsHouse {
    @Override
    public void layingFoundation() {
        System.out.println("平房筑基");
    }

    @Override
    public void buildWall() {
        System.out.println("平房筑墙");
    }

    @Override
    public void sealRoof() {
        System.out.println("平房封顶");
    }
}

main

package com.wangscaler.builder;

/**
 * @author wangscaler
 * @date 2021.06.24 13:56
 */
public class Builder {
    public static void main(String[] args) {
        Bungalow bungalow = new Bungalow();
        bungalow.build();
    }
}

这种方式 ,耦合性较强,将房子的建造过程全部封装在一起,所以需要将房子和房子建造过程进行解耦

产品角色House

package com.wangscaler.builder;

/**
 * @author wangscaler
 * @date 2021.06.23 14:46
 */
public class House {
    private String foundation;
    private String wall;
    private String roof;

    public String getFoundation() {
        return foundation;
    }

    public void setFoundation(String foundation) {
        this.foundation = foundation;
    }

    public String getWall() {
        return wall;
    }

    public void setWall(String wall) {
        this.wall = wall;
    }

    public String getRoof() {
        return roof;
    }

    public void setRoof(String roof) {
        this.roof = roof;
    }
}

抽象的建造者HouseBuilder

package com.wangscaler.builder;


/**
 * @author wangscaler
 * @date 2021.06.23 14:46
 */
public abstract class HouseBuilder {
    protected House house = new House();

    public abstract void layingFoundation();

    public abstract void buildWall();

    public abstract void sealRoof();

    public House buildHouse() {
        return house;
    }
}

具体建造者Bungalow

package com.wangscaler.builder;

/**
 * @author wangscaler
 * @date 2021.06.24 13:56
 */
public class Bungalow extends HouseBuilder {
    @Override
    public void layingFoundation() {
        System.out.println("平房筑基");
    }

    @Override
    public void buildWall() {
        System.out.println("平房筑墙");
    }

    @Override
    public void sealRoof() {
        System.out.println("平房封顶");
    }
}

以及另一个具体的建造者Villa

package com.wangscaler.builder;

/**
 * @author wangscaler
 * @date 2021.06.24 14:49
 */
public class Villa extends HouseBuilder {
    @Override
    public void layingFoundation() {
        System.out.println("别墅筑基");
    }

    @Override
    public void buildWall() {
        System.out.println("别墅筑墙");
    }

    @Override
    public void sealRoof() {
        System.out.println("别墅封顶");
    }
}

指挥者

package com.wangscaler.builder;
/**
 * @author wangscaler
 * @date 2021.06.23 14:46
 */
public class HouseDirector {
    HouseBuilder houseBuilder = null;

    public HouseDirector(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }

    public void setHouseBuilder(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }

    public House constructHouse() {
        houseBuilder.layingFoundation();
        houseBuilder.buildWall();
        houseBuilder.sealRoof();
        return houseBuilder.buildHouse();
    }
}

main

package com.wangscaler.builder;

/**
 * @author wangscaler
 * @date 2021.06.24 13:56
 */
public class Builder {
    public static void main(String[] args) {
        Villa villa = new Villa();
        HouseDirector houseDirector = new HouseDirector(villa);
        House house = houseDirector.constructHouse();
        Bungalow bungalow =new Bungalow();
        houseDirector.setHouseBuilder(bungalow);
        House house1 =houseDirector.constructHouse();
    }
}

当我们增加楼房时,只需要增加TallBuilding即可

package com.wangscaler.builder;
/**
 * @author wangscaler
 * @date 2021.06.24 13:56
 */
public class TallBuilding extends HouseBuilder {
    @Override
    public void layingFoundation() {
        System.out.println("楼房筑基");
    }

    @Override
    public void buildWall() {
        System.out.println("楼房筑墙");
    }

    @Override
    public void sealRoof() {
        System.out.println("楼房封顶");
    }
}

源码中的建造者模式

JDK中的StringBuilder

我们打开源码

package java.lang;

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

    /** use serialVersionUID for interoperability */
    static final long serialVersionUID = 4383685877147921099L;

    /**
     * Constructs a string builder with no characters in it and an
     * initial capacity of 16 characters.
     */
    public StringBuilder() {
        super(16);
    }

    /**
     * Constructs a string builder with no characters in it and an
     * initial capacity specified by the {@code capacity} argument.
     *
     * @param      capacity  the initial capacity.
     * @throws     NegativeArraySizeException  if the {@code capacity}
     *               argument is less than {@code 0}.
     */
    public StringBuilder(int capacity) {
        super(capacity);
    }

    /**
     * Constructs a string builder initialized to the contents of the
     * specified string. The initial capacity of the string builder is
     * {@code 16} plus the length of the string argument.
     *
     * @param   str   the initial contents of the buffer.
     */
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }

    /**
     * Constructs a string builder that contains the same characters
     * as the specified {@code CharSequence}. The initial capacity of
     * the string builder is {@code 16} plus the length of the
     * {@code CharSequence} argument.
     *
     * @param      seq   the sequence to copy.
     */
    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 StringBuilder append(CharSequence s) {
        super.append(s);
        return this;
    }

    /**
     * @throws     IndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder append(CharSequence s, int start, int end) {
        super.append(s, start, end);
        return this;
    }

    @Override
    public StringBuilder append(char[] str) {
        super.append(str);
        return this;
    }

    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder append(char[] str, int offset, int len) {
        super.append(str, offset, len);
        return this;
    }

    @Override
    public StringBuilder append(boolean b) {
        super.append(b);
        return this;
    }

    @Override
    public StringBuilder append(char c) {
        super.append(c);
        return this;
    }

    @Override
    public StringBuilder append(int i) {
        super.append(i);
        return this;
    }

    @Override
    public StringBuilder append(long lng) {
        super.append(lng);
        return this;
    }

    @Override
    public StringBuilder append(float f) {
        super.append(f);
        return this;
    }

    @Override
    public StringBuilder append(double d) {
        super.append(d);
        return this;
    }

    /**
     * @since 1.5
     */
    @Override
    public StringBuilder appendCodePoint(int codePoint) {
        super.appendCodePoint(codePoint);
        return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder delete(int start, int end) {
        super.delete(start, end);
        return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder deleteCharAt(int index) {
        super.deleteCharAt(index);
        return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder replace(int start, int end, String str) {
        super.replace(start, end, str);
        return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder insert(int index, char[] str, int offset,
                                int len)
    {
        super.insert(index, str, offset, len);
        return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder insert(int offset, Object obj) {
            super.insert(offset, obj);
            return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder insert(int offset, String str) {
        super.insert(offset, str);
        return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder insert(int offset, char[] str) {
        super.insert(offset, str);
        return this;
    }

    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder insert(int dstOffset, CharSequence s) {
            super.insert(dstOffset, s);
            return this;
    }

    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder insert(int dstOffset, CharSequence s,
                                int start, int end)
    {
        super.insert(dstOffset, s, start, end);
        return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder insert(int offset, boolean b) {
        super.insert(offset, b);
        return this;
    }

    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder insert(int offset, char c) {
        super.insert(offset, c);
        return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder insert(int offset, int i) {
        super.insert(offset, i);
        return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder insert(int offset, long l) {
        super.insert(offset, l);
        return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder insert(int offset, float f) {
        super.insert(offset, f);
        return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder insert(int offset, double d) {
        super.insert(offset, d);
        return this;
    }

    @Override
    public int indexOf(String str) {
        return super.indexOf(str);
    }

    @Override
    public int indexOf(String str, int fromIndex) {
        return super.indexOf(str, fromIndex);
    }

    @Override
    public int lastIndexOf(String str) {
        return super.lastIndexOf(str);
    }

    @Override
    public int lastIndexOf(String str, int fromIndex) {
        return super.lastIndexOf(str, fromIndex);
    }

    @Override
    public StringBuilder reverse() {
        super.reverse();
        return this;
    }

    @Override
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        s.defaultWriteObject();
        s.writeInt(count);
        s.writeObject(value);
    }

    /**
     * readObject is called to restore the state of the StringBuffer from
     * a stream.
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        count = s.readInt();
        value = (char[]) s.readObject();
    }

}

发现他继承了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];
    }

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

}

以append方法为例,我们发现他是实现的Appendable这个抽象类

/*
 * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 */

package java.lang;

import java.io.IOException;

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

这个Appendable就可以说是我们建造者模式的抽象建造者,在这里创建了抽象方法,那么我们的AbstractStringBuilder就是具体建造者实现了这些抽象方法,StringBuilder既充当了指挥者角色

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

又充当了具体的建造者

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

总结

建造者四种角色

  • 产品角色:包含多个组成部件(筑基、筑墙、封顶)的一个具体的对象(房子)
  • 抽象建造者:包含创建产品各个部件(筑基、筑墙、封顶)的抽象方法的接口,一般还包含返回最终产品的方法(buildHouse)。
  • 具体建造者:实现接口,实现构建装配各个部件的方法(筑基、筑墙、封顶)
  • 指挥者:调用构建部件的方法完成对象的创建(组装房子)。

使用场景:

​ 1、建造者创建的产品一般具有较多的共同点(盖楼都是筑基、筑墙、封顶),如果差异比较大,则不适合使用。

​ 2、创建简单的对象一般使用工厂模式,创建复杂对象(多于5个组件)一般使用建造者模式

​ 3、相同的方法,不同的执行顺序,产生不同的结果。

​ 4、 多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。

​ 5、产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用。

​ 6、初始化一个对象特别复杂,参数多,而且很多参数都具有默认值。

参考资料