转发: The builder&nbs…
2014-04-15 10:41
337 查看
这篇文章真的很好, 让你如何知道怎么把一个很理论化的builder
pattern 用到实际中来。 故分享之
I’m not going to dive into much details about the pattern
because there’s already tons of posts and books that explain it in
fine detail. Instead, I’m going to tell you why and when you should
consider using it. However, it is worth mentioning that this
pattern is a bit different to the one presented in the Gang of Four book. While the original pattern focuses
on abstracting the steps of construction so that by varying the
builder implementation used we can get a different result, the
pattern explained in this post deals with removing the unnecessary
complexity that stems from multiple constructors, multiple optional
parameters and overuse of setters.
Imagine you have a class with a substantial amount of attributes
like the User class below. Let’s assume that you want to
make the class immutable (which, by the way,
unless there’s a really good reason not to you should always strive
to do. But we’ll get to that in a different post).
Now, imagine that some of the attributes in your class are
required while others are optional. How would you go about building
an object of this class? All attributes are declared final so you
have to set them all in the constructor, but you also want to give
the clients of this class the chance of ignoring the optional
attributes.
A first and valid option would be to have a constructor that
only takes the required attributes as parameters, one that takes
all the required attributes plus the first optional one, another
one that takes two optional attributes and so on. What does that
look like? Something like this:
The good thing about this way of building objects of the class
is that it works. However, the problem with this approach should be
pretty obvious. When you only have a couple of attributes is not
such a big deal, but as that number increases the code becomes
harder to read and maintain. More importantly, the code becomes
increasingly harder for clients. Which constructor should I invoke
as a client? The one with 2 parameters? The one with 3? What is the
default value for those parameters where I don’t pass an explicit
value? What if I want to set a value for address but not for age
and phone? In that case I would have to call the constructor that
takes all the parameters and pass default values for those that I
don’t care about. Additionally, several parameters with the same
type can be confusing. Was the first String the phone number or the
address?
So what other choice do we have for these cases? We can always
follow the JavaBeans convention, where we have a default no-arg
constructor and have setters and getters for every attribute.
Something like:
This approach seems easier to read and maintain. As a client I
can just create an empty object and then set only the attributes
that I’m interested in. So what’s wrong with it? There are two main
problems with this solution. The first issue has to do with having
an instance of this class in an inconsistent state. If you want to
create an User object with values for all its 5 attributes
then the object will not have a complete state until all the
setX methods have been invoked. This means that some part
of the client application might see this object and assume that is
already constructed while that’s actually not the case. The second
disadvantage of this approach is that now the User class
is mutable. You’re loosing all the benefits of immutable
objects.
Fortunately there is a third choice for these cases, the builder
pattern. The solution will look something like the following.
A couple of important points worth noting:
The User constructor is private, which means that this class
can not be directly instantiated from the client code.
The class is once again immutable. All attributes are final and
they’re set on the constructor. Additionally, we only provide
getters for them.
The builder uses the Fluent Interface idiom to make the client code more
readable (we’ll see an example of this in a moment).
The builder constructor only receives the required attributes
and this attributes are the only ones that are defined “final” on
the builder to ensure that their values are set on the
constructor.
The use of the builder pattern has all the advantages of the
first two approaches I mentioned at the beginning and none of their
shortcomings. The client code is easier to write and, more
importantly, to read. The only critique that I’ve heard about the
pattern is the fact that you have to duplicate the class’
attributes on the builder. However, given the fact that the builder
class is usually a static member class of the class it builds, they
can evolve together fairly easy.
Now, how does the client code trying to create a new
User object looks like? Let’s see:
Pretty neat, isn’t it? You can build a User object in 1
line of code and, most importantly, is very easy to read. Moreover,
you’re making sure that whenever you get an object of this class is
not going to be on an incomplete state.
This pattern is really flexible. A single builder can be used to
create multiple objects by varying the builder attributes between
calls to the “build” method. The builder could even auto-complete
some generated field between each invocation, such as an id or
serial number.
An important point is that, like a constructor, a builder can
impose invariants on its parameters. The build method can check
these invariants and throw an IllegalStateException if
they are not valid.
It is critical that they be checked after copying the parameters
from the builder to the object, and that they be checked on the
object fields rather than the builder fields. The reason for this
is that, since the builder is not thread-safe, if we check the
parameters before actually creating the object their values can be
changed by another thread between the time the parameters are
checked and the time they are copied. This period of time is known
as the “window of vulnerability”. In our User example this
could look like the following:
The previous version is thread-safe because we first create the
user and then we check the invariants on the immutable object. The
following code looks functionally identical but it’s not
thread-safe and you should avoid doing things like this:
A final advantage of this pattern is that a builder could be
passed to a method to enable this method to create one or more
objects for the client, without the method needing to know any kind
of details about how the objects are created. In order to do this
you would usually have a simple interface like:
In the previous User example, the UserBuilder
class could implement Builder. Then, we could have
something like:
pattern 用到实际中来。 故分享之
I’m not going to dive into much details about the pattern
because there’s already tons of posts and books that explain it in
fine detail. Instead, I’m going to tell you why and when you should
consider using it. However, it is worth mentioning that this
pattern is a bit different to the one presented in the Gang of Four book. While the original pattern focuses
on abstracting the steps of construction so that by varying the
builder implementation used we can get a different result, the
pattern explained in this post deals with removing the unnecessary
complexity that stems from multiple constructors, multiple optional
parameters and overuse of setters.
Imagine you have a class with a substantial amount of attributes
like the User class below. Let’s assume that you want to
make the class immutable (which, by the way,
unless there’s a really good reason not to you should always strive
to do. But we’ll get to that in a different post).
1 | public class User {< 4000 /code> |
2 | private final String firstName; //required |
3 | private final String lastName; //required |
4 | private final int age; //optional |
5 | private final String phone; //optional |
6 | private final String address; //optional |
7 | ... |
8 | } |
required while others are optional. How would you go about building
an object of this class? All attributes are declared final so you
have to set them all in the constructor, but you also want to give
the clients of this class the chance of ignoring the optional
attributes.
A first and valid option would be to have a constructor that
only takes the required attributes as parameters, one that takes
all the required attributes plus the first optional one, another
one that takes two optional attributes and so on. What does that
look like? Something like this:
01 | public User(String firstName, String lastName) { |
02 | this (firstName, lastName, 0 ); |
03 | } |
04 |
05 | public User(String firstName, String lastName, int age) { |
06 | this (firstName, lastName, age, '' ); |
07 | } |
08 |
09 | public User(String firstName, String lastName, int age, String phone) { |
10 | this (firstName, lastName, age, phone, '' ); |
11 | } |
12 |
13 | public User(String firstName, String lastName, int age, String phone, String address) { |
14 | this .firstName = firstName; |
15 | this .lastName = lastName; |
16 | this .age = age; |
17 | this .phone = phone; |
18 | this .address = address; |
19 | } |
is that it works. However, the problem with this approach should be
pretty obvious. When you only have a couple of attributes is not
such a big deal, but as that number increases the code becomes
harder to read and maintain. More importantly, the code becomes
increasingly harder for clients. Which constructor should I invoke
as a client? The one with 2 parameters? The one with 3? What is the
default value for those parameters where I don’t pass an explicit
value? What if I want to set a value for address but not for age
and phone? In that case I would have to call the constructor that
takes all the parameters and pass default values for those that I
don’t care about. Additionally, several parameters with the same
type can be confusing. Was the first String the phone number or the
address?
So what other choice do we have for these cases? We can always
follow the JavaBeans convention, where we have a default no-arg
constructor and have setters and getters for every attribute.
Something like:
01 | public class User { |
02 | private String firstName; // required |
03 | private String lastName; // required |
04 | private int age; // optional |
05 | private String phone; // optional |
06 | private String address; //optional |
07 |
08 | public String getFirstName() { |
09 | return firstName; |
10 | } |
11 | public void setFirstName(String firstName) { |
12 | this .firstName = firstName; |
13 | } |
14 | public String getLastName() { |
15 | return lastName; |
16 | } |
17 | public void setLastName(String lastName) { |
18 | this .lastName = lastName; |
19 | } |
20 | public int getAge() { |
21 | return age; |
22 | } |
23 | public void setAge( int age) { |
24 | this .age = age; |
25 | } |
26 | public String getPhone() { |
27 | return phone; |
28 | } |
29 | public void setPhone(String phone) { |
30 | this .phone = phone; |
31 | } |
32 | public String getAddress() { |
33 | return address; |
34 | } |
35 | public void setAddress(String address) { |
36 | this .address = address; |
37 | } |
38 | } |
can just create an empty object and then set only the attributes
that I’m interested in. So what’s wrong with it? There are two main
problems with this solution. The first issue has to do with having
an instance of this class in an inconsistent state. If you want to
create an User object with values for all its 5 attributes
then the object will not have a complete state until all the
setX methods have been invoked. This means that some part
of the client application might see this object and assume that is
already constructed while that’s actually not the case. The second
disadvantage of this approach is that now the User class
is mutable. You’re loosing all the benefits of immutable
objects.
Fortunately there is a third choice for these cases, the builder
pattern. The solution will look something like the following.
01 | public class User { |
02 | private final String firstName; // required |
03 | private final String lastName; // required |
04 | private final int age; // optional |
05 | private final String phone; // optional |
06 | private final String address; // optional |
07 |
08 | private User(UserBuilder builder) { |
09 | this .firstName = builder.firstName; |
10 | this .lastName = builder.lastName; |
11 | this .age = builder.age; |
12 | this .phone = builder.phone; |
13 | this .address = builder.address; |
14 | } |
15 |
16 | public String getFirstName() { |
17 | return firstName; |
18 | } |
19 |
20 | public String getLastName() { |
21 | return lastName; |
22 | } |
23 |
24 | public int getAge() { |
25 | return age; |
26 | } |
27 |
28 | public String getPhone() { |
29 | return phone; |
30 | } |
31 |
32 | public String getAddress() { |
33 | return address; |
34 | } |
35 |
36 | public static class UserBuilder { |
37 | private final String firstName; |
38 | private final String lastName; |
39 | private int age; |
40 | private String phone; |
41 | private String address; |
42 |
43 | public UserBuilder(String firstName, String lastName) { |
44 | this .firstName = firstName; |
45 | this .lastName = lastName; |
46 | } |
47 |
48 | public UserBuilder age( int age) { |
49 | this .age = age; |
50 | return this ; |
51 | } |
52 |
53 | public UserBuilder phone(String phone) { |
54 | this .phone = phone; |
55 | return this ; |
56 | } |
57 |
58 | public UserBuilder address(String address) { |
59 | this .address = address; |
60 | return this ; |
61 | } |
62 |
63 | public User build() { |
64 | return new User( this ); |
65 | } |
66 |
67 | } |
68 | } |
The User constructor is private, which means that this class
can not be directly instantiated from the client code.
The class is once again immutable. All attributes are final and
they’re set on the constructor. Additionally, we only provide
getters for them.
The builder uses the Fluent Interface idiom to make the client code more
readable (we’ll see an example of this in a moment).
The builder constructor only receives the required attributes
and this attributes are the only ones that are defined “final” on
the builder to ensure that their values are set on the
constructor.
The use of the builder pattern has all the advantages of the
first two approaches I mentioned at the beginning and none of their
shortcomings. The client code is easier to write and, more
importantly, to read. The only critique that I’ve heard about the
pattern is the fact that you have to duplicate the class’
attributes on the builder. However, given the fact that the builder
class is usually a static member class of the class it builds, they
can evolve together fairly easy.
Now, how does the client code trying to create a new
User object looks like? Let’s see:
1 | public User getUser() { |
2 | return new |
3 | User.UserBuilder( 'Jhon' , 'Doe' ) |
4 | .age( 30 ) |
5 | .phone( '1234567' ) |
6 | .address( 'Fake address 1234' ) |
7 | .build(); |
8 | } |
line of code and, most importantly, is very easy to read. Moreover,
you’re making sure that whenever you get an object of this class is
not going to be on an incomplete state.
This pattern is really flexible. A single builder can be used to
create multiple objects by varying the builder attributes between
calls to the “build” method. The builder could even auto-complete
some generated field between each invocation, such as an id or
serial number.
An important point is that, like a constructor, a builder can
impose invariants on its parameters. The build method can check
these invariants and throw an IllegalStateException if
they are not valid.
It is critical that they be checked after copying the parameters
from the builder to the object, and that they be checked on the
object fields rather than the builder fields. The reason for this
is that, since the builder is not thread-safe, if we check the
parameters before actually creating the object their values can be
changed by another thread between the time the parameters are
checked and the time they are copied. This period of time is known
as the “window of vulnerability”. In our User example this
could look like the following:
1 | public User build() { |
2 | User user = new user( this ); |
3 | if (user.getAge() 120 ) { |
4 | throw new IllegalStateException(“Age out of range”); // thread-safe |
5 | } |
6 | return user; |
7 | } |
user and then we check the invariants on the immutable object. The
following code looks functionally identical but it’s not
thread-safe and you should avoid doing things like this:
1 | public User build() { |
2 | if (age 120 ) { |
3 | throw new IllegalStateException(“Age out of range”); // bad, not thread-safe |
4 | } |
5 | // This is the window of opportunity for a second thread to modify the value of age |
6 | return new User( this ); |
7 | } |
passed to a method to enable this method to create one or more
objects for the client, without the method needing to know any kind
of details about how the objects are created. In order to do this
you would usually have a simple interface like:
1 | public interface Builder { |
2 | T build(); |
3 | } |
class could implement Builder. Then, we could have
something like:
1 | UserCollection buildUserCollection(Builder |
相关文章推荐
- storm-Understanding the&nbs…
- the user operation&nbs…
- Retrieving the COM&nbs…
- SVN:cannot&nbsp;map&nbsp;the&amp;nbs…
- customized the date&nb…
- Errors occurred during the build. Errors running builder 'DeploymentBuilder' on project 'myf'. java
- What does the lic…
- Picture&nbsp;of&nbsp;my&nbsp;baby&nbsp;when&nbsp;2&nbsp;monthe…
- The Machine Learning Dictionary
- LaTex使用技巧(10):the memo…
- 爬坡赛: Hill Climb&nbs…
- 宏基 ACER A500/501&nbs…
- VC助手出问题了:All instances of the lincense "ren"&nbs
- poj&nbsp;3083&nbsp;Children&nbsp;of&nbsp;the&nbsp;Candy&nbsp;C…
- 世界末日 This is the End (2013)
- HDU 1010 Tempter of the Bone
- adb报错:The connection to adb is down, and a severe&nbs
- The&nbsp;Python&nbsp;Tutorial&nbsp;&raquo;&nbsp;5.&nbsp;D…
- ZOJ&nbsp;1053&nbsp;FDNY&nbsp;to&nbsp;the&nbsp;Rescue
- ZOJ&nbsp;1056&nbsp;The&nbsp;Worm&nbsp;Turns