public
Created

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/

  • Download Gist
PizzaOrder.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
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;
}
}
PizzaOrderOldStyle.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
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;
}
 
}
PizzaOrderOldStyleTest.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
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());
}
}
PizzaOrderTest.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
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");
}
 
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.