Senior Software Developer at Citrix Systems Luis is a DZone MVB and is not an employee of DZone and has posted 10 posts at DZone. You can read more from them at their website. View Full User Profile

Fluent Object Creation

01.24.2013
| 3268 views |
  • submit to reddit

Many posts have been written on this subject (overwhelmingly many) but I just wanted to contribute my two-cents and write a short post about how I use the Fluent Object Creation pattern or object builders in Java to instantiate Value Objects.

Value Objects
 are abstractions that are defined by their state (value) rather than their address in memory. Examples of value objects are things like money, a number, a coordinate, etc. They are used not to describe Business Objects but rather descriptions of concrete indivisible entities.  Also, they make great candidates for adding them to collections and maps.

In Java, Value Objects should be declared final and provide no setter methods, basically making it's state immutable after creation, this is a very important requirement. Declaring them final makes them unable to serve as parent objects. This is done by design since value objects should model small and concrete entities. The reason being is that we should be able to create and compare multiple copies of these objects, which is always done by state not by reference. In addition, you should declare proper equals() and hashCode() methods to qualify for a proper value object.


In C++, the same principles apply. In C++ you should make use of the Copy Constructor and overload the assignment and comparison operators. 


The Fluent Object Creation pattern makes value object instantiation elegant and clean. There are many benefits that can be gained by using fluent object creation as we will see shortly.  The end result of applying this pattern from the API user's perspective will look like the following:
Money fiveEuros = new Money.Builder()
   .currency(Currency.EURO)
   .value(5.0L)
   .countryOfOrigin("Spain")
   .type("Coin")
   .reverse("Map of Europe")
   .obverse("Map of Spain")
   .addColor("Bronze")
   .addColor("Silver")
   .year("1880")
.build();
I think you would agree that this pattern flows a lot more smoother as opposed to this:
Money fiveEuros = new Money();
fiveEuros.setCurrency(Currency.EURO);
fiveEuros.setValue(5.0L);
fiveEuros.countryOfOrigin("Spain");
fiveEuros.type("Coin");
fiveEuros.reverse("Map of Europe");
fiveEuros.obverse("Map of Spain");

List<String> colors = new ArrayList<String>();
for(String color: new String[] {"Bronze", "Silver"}) {
   colors.add(color);
}

fiveEuros.setColors(colors);
fiveEuros.setYear("1880");
Which seems broken and has lots of typing and repetition. This is an example of building a pretty big  value object in my opinion, most of tend to be very small.

Before we talk about the benefits of creating objects this way, let's have a look at the structure of this pattern:
 public final class Money {
   
      private Long value;
      private String countryOfOrigin;     
      private Currency currency;
      private String type; 
      private String reverse;
      private String obverse;    
      private List<String> colors;
   private Date year;    
 
      private Money() {   }

     // -- getters, hashCode, equals -- //

     // Static inner Builder class
   public static class Builder {
        private Money _temp = new Money();
public Builder value(Long val) {
_temp.value = val;
return this;
}
public Builder countryOfOrigin(String countryOfOrigin) {
_temp.contryOfOrigin = countryOfOrigin;
         return this;
}
public Builder currency(Currency c) {
_temp.currency = c;
return this;
}
public Builder type(String t) {
_temp.type = t;
return this;
}
public Builder reverse(String r) {
_temp.reverse = r;
return this;
}


       public Builder obverse(String o) {
_temp.obverse = o;
return this;
}


public Builder addColor(String c) {
if(_temp.colors == null) {
              _temp.colors = new ArrayList<String>(); 
           }
           _temp.colors.add(c);
return this;
}

         public Builder year(String y) {
              if(y == null || y.isEmpty()) {
                  _temp.year = new Date();
              }
              else {
                  _temp.year = DateFormat.parse(y);
              }
return this;
}
public Money build() {
                // Validate object
               if(Strings.isNullOrEmpty(_temp.name) || _temp.currency == null) {
                  throw new IllegalArgumentException("Coin currency and value required");
               }
return _temp;
}
    }
}
This is also a matter of taste, but I prefer the static inner class approach. I like the canonical nature of referring to the builder as Money.Builder. Also making it static is required since the builder instance needs to live independently of the enclosing class.

I like this pattern because it has the following benefits:
  1. Greater object encapsulation: I can easily enforce object construction using builders by making the Money constructor private (this is just stylistic). This completely hides all of the intricacies of creating this object: list creation, date parsing, etc. From the user's perspective, what we end up with is an object that is simple to instantiate. My illustration is a very simple one, but imagine more complex object graphs.
  2. Code readability: Using this pattern to create objects, makes unit tests and code very easy to read and follow. 
  3. Less typing in the long run: Even though you have to add an extra builder method for every added attributes, the amount of typing you save in the long run makes it worth while. 

Conclusion
Using the fluent creation pattern is more work up front, but the benefits of having it pays off at the end. It makes instantiating objects very elegant and clean. You don't have to use it with Value Objects, most of the benefit of using Fluent Object Creation is when you need to build pretty complex object graphs, but I wanted to show that it can also suitsmall value objects.



 

Published at DZone with permission of Luis Atencio, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Andrew Gilmartin replied on Fri, 2013/01/25 - 8:03am

Well said.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.