Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5dad7af

Browse filesBrowse files
author
Nicolai Parlog
committed
[02] Demonstrate builders and show alternatives
1 parent 09c4e1d commit 5dad7af
Copy full SHA for 5dad7af

File tree

Expand file treeCollapse file tree

15 files changed

+769
-0
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

15 files changed

+769
-0
lines changed
Open diff view settings
Collapse file

‎README.md‎

Copy file name to clipboardExpand all lines: README.md
+1Lines changed: 1 addition & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ Related links:
1414

1515
* Item 1: [Consider static factory methods instead of constructors](https://www.youtube.com/watch?v=WUROOKn2OTk) -
1616
[examples](src/main/java/org/codefx/demo/effective_java/_01_static_factory_methods/Main.java)
17+
* Item 2: [Consider a builder when faced with many constructor parameters](https://www.youtube.com/watch?v=2GMp8VuxZnw)
Collapse file
+62Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package org.codefx.demo.effective_java._02_builder_pattern;
2+
3+
import org.codefx.demo.effective_java._02_builder_pattern.SelfTypes.Employee;
4+
import org.codefx.demo.effective_java._02_builder_pattern.SelfTypes.EmployeeBuilder;
5+
import org.codefx.demo.effective_java._02_builder_pattern.SelfTypes.Freelancer;
6+
import org.codefx.demo.effective_java._02_builder_pattern.SelfTypes.FreelancerBuilder;
7+
import org.codefx.demo.effective_java._02_builder_pattern.TowardsDsl.Body;
8+
import org.codefx.demo.effective_java._02_builder_pattern.TowardsDsl.Car;
9+
import org.codefx.demo.effective_java._02_builder_pattern.TowardsDsl.CarFactory;
10+
import org.codefx.demo.effective_java._02_builder_pattern.TowardsDsl.ConvertibleBody;
11+
import org.codefx.demo.effective_java._02_builder_pattern.TowardsDsl.Decal;
12+
import org.codefx.demo.effective_java._02_builder_pattern.TowardsDsl.Paint;
13+
import org.codefx.demo.effective_java._02_builder_pattern.TowardsDsl.RearSpoiler;
14+
import org.codefx.demo.effective_java._02_builder_pattern.TowardsDsl.RoofSpoiler;
15+
import org.codefx.demo.effective_java._02_builder_pattern.TowardsDsl.Tires;
16+
17+
/*
18+
* You will find NOTEs on the following topics:
19+
* - alternatives
20+
* - DSL
21+
* - self type
22+
* - refactoring
23+
*/
24+
25+
public class Main {
26+
27+
public static void main(String[] args) {
28+
towardsDsl();
29+
selfType();
30+
}
31+
32+
private static void towardsDsl() {
33+
Car car = new CarFactory()
34+
.body(new Body())
35+
.spoiler(new RoofSpoiler())
36+
.paint(new Paint())
37+
.decal(new Decal())
38+
.tires(new Tires())
39+
.build();
40+
41+
Car convertible = new CarFactory()
42+
.body(new ConvertibleBody())
43+
// can't add a roof spoiler!
44+
.spoiler(new RearSpoiler())
45+
.paint(new Paint())
46+
.decal(new Decal())
47+
.tires(new Tires())
48+
.build();
49+
}
50+
51+
private static void selfType() {
52+
Employee john = new EmployeeBuilder()
53+
.name("John Doe")
54+
.address("Paris")
55+
.build();
56+
Freelancer jane = new FreelancerBuilder()
57+
.name("Jane Doe")
58+
.address("Paris")
59+
.build();
60+
}
61+
62+
}
Collapse file
+17Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
package org.codefx.demo.effective_java._02_builder_pattern
3+
4+
class NutritionFacts(
5+
val servingSize: Int,
6+
val servings: Int,
7+
val fat: Int = 0,
8+
val sodium: Int = 0,
9+
val carbohydrates: Int = 0) { }
10+
11+
fun main(args: Array<String>) {
12+
val facts = NutritionFacts(
13+
servingSize = 150, servings = 4,
14+
fat = 20, carbohydrates = 25);
15+
}
16+
17+
*/
Collapse file
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.codefx.demo.effective_java._02_builder_pattern;
2+
3+
// NOTE refactoring: Your IDE should help you refactor from constructor to builder.
4+
public class RefactorToBuilder {
5+
6+
public static void main(String[] args) {
7+
Person p = new Person("Jane Doe");
8+
System.out.println(p);
9+
}
10+
11+
public static class Person {
12+
13+
private final String name;
14+
15+
public Person(String name) {
16+
this.name = name;
17+
}
18+
19+
}
20+
21+
}
Collapse file
+97Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package org.codefx.demo.effective_java._02_builder_pattern;
2+
3+
public class SelfTypes {
4+
5+
public abstract static class Person {
6+
7+
private final String name;
8+
private final String address;
9+
10+
protected Person(PersonBuilder<?, ?> builder) {
11+
this.name = builder.name;
12+
this.address = builder.address;
13+
}
14+
15+
public String name() {
16+
return name;
17+
}
18+
19+
public String address() {
20+
return address;
21+
}
22+
23+
}
24+
25+
public static class Employee extends Person {
26+
27+
private Employee(EmployeeBuilder builder) {
28+
super(builder);
29+
}
30+
31+
}
32+
33+
public static class Freelancer extends Person {
34+
35+
private Freelancer(FreelancerBuilder builder) {
36+
super(builder);
37+
}
38+
39+
}
40+
41+
// NOTE self type:
42+
// With a recursive generic type `SELF extends PersonBuilder<SELF>` (ignoring `P` for a moment,
43+
// which does not pertain to this), it is possible to capture "type of this instance".
44+
public static abstract class PersonBuilder<P extends Person, SELF extends PersonBuilder<P, SELF>> {
45+
46+
protected String name;
47+
protected String address;
48+
49+
// NOTE self type:
50+
// Thanks to `SELF`, `name(String)` can express that it returns not just a `PersonBuilder`,
51+
// but an instance of the concrete builder it was called on.
52+
public SELF name(String name) {
53+
this.name = name;
54+
// NOTE self type:
55+
// This cast is safe, so I find it tolerable. See below for an alternative.
56+
return (SELF) this;
57+
}
58+
59+
// NOTE self type:
60+
// To avoid the casts `(SELF) this`, call this method instead.
61+
// protected abstract SELF self();
62+
63+
public SELF address(String address) {
64+
this.address = address;
65+
return (SELF) this;
66+
}
67+
68+
public abstract P build();
69+
70+
}
71+
72+
public static class EmployeeBuilder extends PersonBuilder<Employee, EmployeeBuilder> {
73+
74+
@Override
75+
public Employee build() {
76+
return new Employee(this);
77+
}
78+
79+
// NOTE self type:
80+
// Here's how to implement `self()` in a concrete builder.
81+
// @Override
82+
// protected EmployeeBuilder self() {
83+
// return this;
84+
// }
85+
86+
}
87+
88+
public static class FreelancerBuilder extends PersonBuilder<Freelancer, FreelancerBuilder> {
89+
90+
@Override
91+
public Freelancer build() {
92+
return new Freelancer(this);
93+
}
94+
95+
}
96+
97+
}
Collapse file
+148Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package org.codefx.demo.effective_java._02_builder_pattern;
2+
3+
// NOTE DSL:
4+
// The common approach to a builder is to have a single class, on which `build()` can be called
5+
// at any time. If the provided information was incomplete, a `RuntimeException` is the result.
6+
//
7+
// The `CarFactory` in this class follows a more powerful/complex approach where each required
8+
// "setter" transitions to a new type of builder and only the last one has a `build()` method.
9+
// This way, other constructors/methods can use types for their parameters that represent a
10+
// specific "phase" of the build process. The progression from empty to complete builder is thus
11+
// encoded in the type system, which means the compiler can check errors that would otherwise
12+
// only be discovered at run time.
13+
public class TowardsDsl {
14+
15+
public static class Car {
16+
17+
private final Body body;
18+
private final Spoiler spoiler;
19+
private final Paint paint;
20+
private final Decal decal;
21+
private final Tires tires;
22+
23+
private Car(WithPaintedBodyAndTires builder) {
24+
this.body = builder.body;
25+
this.spoiler = builder.spoiler;
26+
this.paint = builder.paint;
27+
this.decal = builder.decal;
28+
this.tires = builder.tires;
29+
}
30+
31+
}
32+
33+
public static class Body { }
34+
public static class ConvertibleBody extends Body { }
35+
36+
public static class Spoiler { }
37+
public static class RoofSpoiler extends Spoiler { }
38+
public static class RearSpoiler extends Spoiler { }
39+
40+
public static class Paint { }
41+
42+
public static class Decal { }
43+
44+
public static class Tires { }
45+
46+
public static class CarFactory {
47+
48+
// NOTE DSL: This method returns `WithBody` instead of a `CarFactory`.
49+
public WithBody body(Body body) {
50+
return new WithBody(body);
51+
}
52+
53+
public WithConvertibleBody body(ConvertibleBody body) {
54+
return new WithConvertibleBody(body);
55+
}
56+
57+
// NOTE self type:
58+
// There is no `build()` method here, because without body, the car is incomplete.
59+
60+
}
61+
62+
public static class WithBody {
63+
64+
private final Body body;
65+
private Spoiler spoiler;
66+
67+
public WithBody(Body body) {
68+
this.body = body;
69+
}
70+
71+
public WithBody spoiler(Spoiler spoiler) {
72+
this.spoiler = spoiler;
73+
return this;
74+
}
75+
76+
public WithPaintedBody paint(Paint paint) {
77+
return new WithPaintedBody(body, spoiler, paint);
78+
}
79+
80+
}
81+
82+
public static class WithConvertibleBody {
83+
84+
private final ConvertibleBody body;
85+
private RearSpoiler spoiler;
86+
87+
public WithConvertibleBody(ConvertibleBody body) {
88+
this.body = body;
89+
}
90+
91+
public WithConvertibleBody spoiler(RearSpoiler spoiler) {
92+
this.spoiler = spoiler;
93+
return this;
94+
}
95+
96+
public WithPaintedBody paint(Paint paint) {
97+
return new WithPaintedBody(body, spoiler, paint);
98+
}
99+
100+
}
101+
102+
public static class WithPaintedBody {
103+
104+
private final Body body;
105+
private final Spoiler spoiler;
106+
private final Paint paint;
107+
private Decal decal;
108+
109+
public WithPaintedBody(Body body, Spoiler spoiler, Paint paint) {
110+
this.body = body;
111+
this.spoiler = spoiler;
112+
this.paint = paint;
113+
}
114+
115+
public WithPaintedBody decal(Decal decal) {
116+
this.decal = decal;
117+
return this;
118+
}
119+
120+
public WithPaintedBodyAndTires tires(Tires tires) {
121+
return new WithPaintedBodyAndTires(body, spoiler, paint, decal, tires);
122+
}
123+
124+
}
125+
126+
public static class WithPaintedBodyAndTires {
127+
128+
private final Body body;
129+
private final Spoiler spoiler;
130+
private final Paint paint;
131+
private final Decal decal;
132+
private final Tires tires;
133+
134+
public WithPaintedBodyAndTires(Body body, Spoiler spoiler, Paint paint, Decal decal, Tires tires) {
135+
this.body = body;
136+
this.spoiler = spoiler;
137+
this.paint = paint;
138+
this.tires = tires;
139+
this.decal = decal;
140+
}
141+
142+
public Car build() {
143+
return new Car(this);
144+
}
145+
146+
}
147+
148+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.