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/
推荐阅读
-
PHP设计模式之工厂模式(Factory Pattern)的讲解
-
C#策略模式(Strategy Pattern)实例教程
-
C#对象为Null模式(Null Object Pattern)实例教程
-
C#单例模式(Singleton Pattern)实例教程
-
C#装饰器模式(Decorator Pattern)实例教程
-
C#模板方法模式(Template Method Pattern)实例教程
-
C#观察者模式(Observer Pattern)实例教程
-
C#迭代器模式(Iterator Pattern)实例教程
-
C#备忘录模式(Memento Pattern)实例教程
-
C#命令模式(Command Pattern)实例教程