Design Pattern: Builder Pattern

Builder Pattern:

     The builder pattern is a design pattern that allows for the step-by-step creation of complex objects using the correct sequence of actions.

     The construction is controlled by a director object that only needs to know the type of object it is to create.

1. Motivation

    The more complex an application is, the complexity of classes and objects used increases.

    Complex objects are made of parts produced by other objects that need special care when being built.

    An application might need a mechanism for building complex objects that is independent from the ones that make up the object.

    The pattern allows a client object to construct a complex object by specifying only its type and content, being shielded from the details related to the object's representation.

    The way the construction process can be used to create different representations.

    The logic of this process is isolated from the actual steps used in creating the complex object,

    so the process can be used again to create a different object from the same set of simple objects as the first one.

2. Intent

    1> Defines an instance for creating an object but letting subclasses decide which class to instantiate.

    2> Refers to the newly created object through a common interface.

3. Example

    1> Simple Example

package edu.xmu.oop.builder;

public class Person {
    private final String lastName; // required
    private final String firstName; // required
    private final String middleName; // optional
    private final String salutation;// optional
    private final String suffix;// optional
    private final String streetAddress;// optional
    private final String city;// optional
    private final String state;// optional
    private final boolean isEmployed;// optional
    private final boolean isFemale;// required
    private final boolean isHomewOwner;// optional

    private Person(Builder personBuilder) {
	this.lastName = personBuilder.lastName;
	this.firstName = personBuilder.firstName;
	this.middleName = personBuilder.middleName;
	this.salutation = personBuilder.salutation;
	this.suffix = personBuilder.suffix;
	this.streetAddress = personBuilder.streetAddress;
	this.city = personBuilder.city;
	this.state = personBuilder.state;
	this.isEmployed = personBuilder.isEmployed;
	this.isFemale = personBuilder.isFemale;
	this.isHomewOwner = personBuilder.isHomewOwner;
    }

    public static class Builder {
	private final String lastName; // required
	private final String firstName; // required
	private String middleName = ""; // optional
	private String salutation = "";// optional
	private String suffix = "";// optional
	private String streetAddress = "";// optional
	private String city = "";// optional
	private String state = "";// optional
	private final boolean isFemale;// required
	private boolean isEmployed = false;// optional
	private boolean isHomewOwner = false;// optional

	public Builder(String lastName, String firstName, boolean isFemale) {
	    super();
	    this.lastName = lastName;
	    this.firstName = firstName;
	    this.isFemale = isFemale;
	}

	public Builder setMiddleName(String middleName) {
	    this.middleName = middleName;
	    return this;
	}

	public Builder setSalutation(String salutation) {
	    this.salutation = salutation;
	    return this;
	}

	public Builder setSuffix(String suffix) {
	    this.suffix = suffix;
	    return this;
	}

	public Builder setStreetAddress(String streetAddress) {
	    this.streetAddress = streetAddress;
	    return this;
	}

	public Builder setCity(String city) {
	    this.city = city;
	    return this;
	}

	public Builder setState(String state) {
	    this.state = state;
	    return this;
	}

	public Builder setEmployed(boolean isEmployed) {
	    this.isEmployed = isEmployed;
	    return this;
	}

	public Builder setHomewOwner(boolean isHomewOwner) {
	    this.isHomewOwner = isHomewOwner;
	    return this;
	}

	public Person build() {
	    return new Person(this);
	}
    }

    @Override
    public String toString() {
	return "Person [lastName=" + lastName + ", firstName=" + firstName
		+ ", middleName=" + middleName + ", salutation=" + salutation
		+ ", suffix=" + suffix + ", streetAddress=" + streetAddress
		+ ", city=" + city + ", state=" + state + ", isEmployed="
		+ isEmployed + ", isFemale=" + isFemale + ", isHomewOwner="
		+ isHomewOwner + "]";
    }

}
package edu.xmu.oop.builder;

import org.apache.log4j.Logger;
import org.junit.Test;

public class PersonTest {
    private static final Logger logger = Logger.getLogger(PersonTest.class);

    @Test
    public void buildTest() {
	Person p = new Person.Builder("Yang", "Kunlun", false)
		.setCity("Shanghai").setEmployed(true)
		.setStreetAddress("Chenhui RD").build();
	logger.info(p);
    }
}

    Attention:
        1> Person.Build has to be public static for outter class to have access to this Builder class

        2> Person.Build has to have the same attributes as Person have and have to unfinalize the optional attributes.

        3> Person's constructor has to be private in that outter class cannot construct Person directly.

    2> Guava Example

package edu.xmu.oop.builder;

public class ImmutableList<E> {
    private final transient Object[] array;

    private ImmutableList(Object[] array) {
	this.array = array;
    }

    public static <E> Builder<E> builder() {
	return new Builder<E>();
    }

    public static final class Builder<E> {
	static final int DEFAULT_INITIAL_CAPACITY = 4;

	Object[] contents;
	int size;

	public Builder() {
	    this(DEFAULT_INITIAL_CAPACITY);
	}

	Builder(int initialCapacity) {
	    this.contents = new Object[initialCapacity];
	    this.size = 0;
	}

	public Builder<E> add(E element) {
	    ensureCapacity(size + 1);
	    contents[size++] = element;
	    return this;
	}

	private void ensureCapacity(int minCapacity) {
	    // ...
	}

	@SuppressWarnings("unchecked")
	public ImmutableList<E> build() {
	    switch (size) {
	    case 0:
		return (ImmutableList<E>) new ImmutableList<Object>(
			new Object[0]); // return an empty list
	    default:
		if (size < contents.length) {
		    contents = arraysCopyOf(contents, size);
		}
		return new ImmutableList<E>(contents);
	    }
	}

	private Object[] arraysCopyOf(Object[] contents2, int size2) {
	    // ...
	    return new Object[0];
	}
    }

}
package edu.xmu.guava.collection;

import static org.junit.Assert.assertEquals;

import java.util.List;

import org.junit.Test;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

public class ImmutableListTest {
    @Test
    public void immutableTest() {
	List<String> strList = ImmutableList.<String> builder().add("A")
		.add("B", "C").build();

	assertEquals(Lists.newArrayList("A", "B", "C"), strList);
    }
}

4. Conclusion

    1) Benefits and Advantages:

        1> The client code benefits greatly in terms of usability and readability.The parameters to the constructor are reduced and are provided in highly readable method calls.

        2> The ability to acquire an object in a single statement and state without the object in multiple states problem presented by using "set" methods.

             The Builder pattern is perfectly suited for an immutable class when that class features a large number of attributes

             and there is no need to pass in null for optional parameters to the constructor.

    2) Costs and Disadvantages:

        1> The number of lines of code of a given class must be essentially doubled for "set" method.

5. An interesting example to talk about:

    http://blog.frankel.ch/a-dive-into-the-builder-pattern#comment-7776

    Comments:

    1> If we need Builder Pattern, that usually means we are working with immutable classes(all properties are final).

    2> If we need mandatory fields, we should make that part of the Builder's constructor instead of using InvalidBuilder&ValidBuilder which would make code increasingly redundancy.

Reference Links:

1) http://www.oodesign.com/builder-pattern.html

2) http://www.javacodegeeks.com/2013/10/too-many-parameters-in-java-methods-part-3-builder-pattern.html

3) Effective Java 2nd Edition -Joshua Blosh

4) Guava Source Code -Kevin Bourrillion

5) http://www.ibm.com/developerworks/library/j-jtp02183/

猜你喜欢

转载自davyjones2010.iteye.com/blog/2094565