Skip to content

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Code showing alternative to the standard Builder pattern implementation in Java. Also see blogpost at http://branchandbound.net/blog/java/2013/10/failed-experiment-improving-builder-pattern/
package net.branchandbound.builder;
import java.util.concurrent.atomic.AtomicBoolean;
public class PizzaOrder {
private int size;
private boolean pepperoni;
private boolean chicken;
private boolean mushroom;
private boolean peppers;
private String cheese;
private String sauce;
private String orderFor;
private PizzaOrder() {
// Prevent direct instantiation
}
public static Builder pizzaOrder(int size, String sauce, String orderFor) {
return new PizzaOrder().new Builder(size, sauce, orderFor);
}
public class Builder {
private AtomicBoolean build = new AtomicBoolean(false);
public Builder(int _size, String _sauce, String _orderFor) {
size = _size;
sauce = _sauce;
orderFor = _orderFor;
}
public Builder withPepperoni() {
throwIfBuild();
pepperoni = true;
return this;
}
public Builder withChicken() {
throwIfBuild();
chicken = true;
return this;
}
public Builder withMushroom() {
throwIfBuild();
mushroom = true;
return this;
}
public Builder withPeppers() {
throwIfBuild();
peppers = true;
return this;
}
public Builder withCheese(String _cheese) {
throwIfBuild();
cheese = _cheese;
return this;
}
public PizzaOrder build() {
if (build.compareAndSet(false, true)) {
// check consistency here...
return PizzaOrder.this;
} else {
throw new IllegalStateException("Build may only be called once!");
}
}
private void throwIfBuild() {
if (build.get()) {
throw new IllegalStateException("Cannot modify builder after calling build()");
}
}
}
public int getSize() {
return size;
}
public boolean isPepperoni() {
return pepperoni;
}
public boolean isChicken() {
return chicken;
}
public boolean isMushroom() {
return mushroom;
}
public boolean isPeppers() {
return peppers;
}
public String getCheese() {
return cheese;
}
public String getSauce() {
return sauce;
}
public String getOrderFor() {
return orderFor;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((cheese == null) ? 0 : cheese.hashCode());
result = prime * result + (chicken ? 1231 : 1237);
result = prime * result + (mushroom ? 1231 : 1237);
result = prime * result + ((orderFor == null) ? 0 : orderFor.hashCode());
result = prime * result + (pepperoni ? 1231 : 1237);
result = prime * result + (peppers ? 1231 : 1237);
result = prime * result + ((sauce == null) ? 0 : sauce.hashCode());
result = prime * result + size;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PizzaOrder other = (PizzaOrder) obj;
if (cheese == null) {
if (other.cheese != null)
return false;
} else if (!cheese.equals(other.cheese))
return false;
if (chicken != other.chicken)
return false;
if (mushroom != other.mushroom)
return false;
if (orderFor == null) {
if (other.orderFor != null)
return false;
} else if (!orderFor.equals(other.orderFor))
return false;
if (pepperoni != other.pepperoni)
return false;
if (peppers != other.peppers)
return false;
if (sauce == null) {
if (other.sauce != null)
return false;
} else if (!sauce.equals(other.sauce))
return false;
if (size != other.size)
return false;
return true;
}
}
package net.branchandbound.builder;
public class PizzaOrderOldStyle {
private int size;
private boolean pepperoni;
private boolean chicken;
private boolean mushroom;
private boolean peppers;
private String cheese;
private String sauce;
private String orderFor;
public static Builder pizzaOrder(int size, String sauce, String orderFor) {
return new PizzaOrderOldStyle.Builder(size, sauce, orderFor);
}
public static class Builder {
private int size;
private boolean pepperoni;
private boolean chicken;
private boolean mushroom;
private boolean peppers;
String cheese;
private String sauce;
private String orderFor;
public Builder(int size, String sauce, String orderFor) {
this.size = size;
this.sauce = sauce;
this.orderFor = orderFor;
}
public Builder withPepperoni() {
this.pepperoni = true;
return this;
}
public Builder withChicken() {
this.chicken = true;
return this;
}
public Builder withMushroom() {
this.mushroom = true;
return this;
}
public Builder withPeppers() {
this.peppers = true;
return this;
}
public Builder withCheese(String cheese) {
this.cheese = cheese;
return this;
}
public PizzaOrderOldStyle build() {
// check consistency here...
return new PizzaOrderOldStyle(this);
}
}
private PizzaOrderOldStyle(Builder builder) {
size = builder.size;
pepperoni = builder.pepperoni;
chicken = builder.chicken;
mushroom = builder.mushroom;
peppers = builder.peppers;
cheese = builder.cheese;
sauce = builder.sauce;
orderFor = builder.orderFor;
}
public int getSize() {
return size;
}
public boolean isPepperoni() {
return pepperoni;
}
public boolean isChicken() {
return chicken;
}
public boolean isMushroom() {
return mushroom;
}
public boolean isPeppers() {
return peppers;
}
public String getCheese() {
return cheese;
}
public String getSauce() {
return sauce;
}
public String getOrderFor() {
return orderFor;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((cheese == null) ? 0 : cheese.hashCode());
result = prime * result + (chicken ? 1231 : 1237);
result = prime * result + (mushroom ? 1231 : 1237);
result = prime * result + ((orderFor == null) ? 0 : orderFor.hashCode());
result = prime * result + (pepperoni ? 1231 : 1237);
result = prime * result + (peppers ? 1231 : 1237);
result = prime * result + ((sauce == null) ? 0 : sauce.hashCode());
result = prime * result + size;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PizzaOrderOldStyle other = (PizzaOrderOldStyle) obj;
if (cheese == null) {
if (other.cheese != null)
return false;
} else if (!cheese.equals(other.cheese))
return false;
if (chicken != other.chicken)
return false;
if (mushroom != other.mushroom)
return false;
if (orderFor == null) {
if (other.orderFor != null)
return false;
} else if (!orderFor.equals(other.orderFor))
return false;
if (pepperoni != other.pepperoni)
return false;
if (peppers != other.peppers)
return false;
if (sauce == null) {
if (other.sauce != null)
return false;
} else if (!sauce.equals(other.sauce))
return false;
if (size != other.size)
return false;
return true;
}
}
package net.branchandbound.builder.test;
import static net.branchandbound.builder.PizzaOrderOldStyle.pizzaOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import net.branchandbound.builder.PizzaOrderOldStyle;
import net.branchandbound.builder.PizzaOrderOldStyle.Builder;
import org.junit.Test;
public class PizzaOrderOldStyleTest {
@Test
public void valuesShouldBeRetained() {
PizzaOrderOldStyle pizzaOrder =
pizzaOrder(10, "tomato", "Sander")
.withPepperoni()
.withMushroom()
.withCheese("parmesan").build();
assertEquals(10, pizzaOrder.getSize());
assertEquals("tomato", pizzaOrder.getSauce());
assertEquals("Sander", pizzaOrder.getOrderFor());
assertTrue(pizzaOrder.isPepperoni());
assertFalse(pizzaOrder.isChicken());
assertTrue(pizzaOrder.isMushroom());
assertFalse(pizzaOrder.isPeppers());
assertEquals("parmesan", pizzaOrder.getCheese());
}
@Test
public void testEqualsHashCode() {
Builder builder = pizzaOrder(10, "tomato", "Sander").withPepperoni().withMushroom().withCheese("parmesan");
PizzaOrderOldStyle a = builder.build();
PizzaOrderOldStyle b = builder.build();
assertEquals(a, b);
assertEquals(a.hashCode(), b.hashCode());
}
}
package net.branchandbound.builder.test;
import static net.branchandbound.builder.PizzaOrder.pizzaOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import net.branchandbound.builder.PizzaOrder;
import net.branchandbound.builder.PizzaOrder.Builder;
import org.junit.Test;
public class PizzaOrderTest {
@Test
public void valuesShouldBeRetained() {
PizzaOrder pizzaOrder =
pizzaOrder(10, "tomato", "Sander").withPepperoni().withMushroom()
.withCheese("parmesan").build();
assertEquals(10, pizzaOrder.getSize());
assertEquals("tomato", pizzaOrder.getSauce());
assertEquals("Sander", pizzaOrder.getOrderFor());
assertTrue(pizzaOrder.isPepperoni());
assertFalse(pizzaOrder.isChicken());
assertTrue(pizzaOrder.isMushroom());
assertFalse(pizzaOrder.isPeppers());
assertEquals("parmesan", pizzaOrder.getCheese());
}
@Test
public void testEqualsHashCode() {
PizzaOrder a = getCorrectlyFilledBuilder().build();
PizzaOrder b = getCorrectlyFilledBuilder().build();
assertEquals(a, b);
assertEquals(a.hashCode(), b.hashCode());
}
@Test(expected = IllegalStateException.class)
public void cannotBuildTwice() {
Builder builder = getCorrectlyFilledBuilder();
builder.build();
builder.build();
}
@Test(expected = IllegalStateException.class)
public void cannotUseBuilderAfterBuild() {
Builder correctlyFilledBuilder = getCorrectlyFilledBuilder();
correctlyFilledBuilder.build();
correctlyFilledBuilder.withCheese("FAIL");
}
private Builder getCorrectlyFilledBuilder() {
return pizzaOrder(10, "tomato", "Sander").withPepperoni().withMushroom()
.withCheese("parmesan");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.