Implement builder pattern in generated record classes that sets default values 
when omitted
-------------------------------------------------------------------------------------------

                 Key: AVRO-839
                 URL: https://issues.apache.org/jira/browse/AVRO-839
             Project: Avro
          Issue Type: Improvement
          Components: java
            Reporter: James Baldassari
            Assignee: James Baldassari


This is an idea for an improvement to the SpecificCompiler-generated record 
classes.  There are two main issues to address:

# Default values specified in schemas are only used at read time, not when 
writing/serializing records.  For example, a NullPointerException is thrown 
when attempting to write a record that has an uninitialized array or string 
type.  I'm sure this was done for good reasons, like giving users maximum 
control and preventing unnecessary garbage collection, but I think it's also 
somewhat confusing and unintuitive for new users (myself included).
# Users have to create their own factory classes/methods for every record type, 
both to ensure that all non-primitive members are initialized and to facilitate 
the construction and initialization of record instances (i.e. constructing and 
setting values in a single statement).

These issues have been discussed previously here:
* [http://search-hadoop.com/m/iDVTn1JVeSR1]
* AVRO-726
* AVRO-770
* [http://search-hadoop.com/m/JuY1V16pwxh1]

I'd like to propose a solution that is used by at least one other messaging 
framework.  For each generated record class there will be a public static inner 
class called Builder.  The Builder inner class has the same fields as the 
record class, as well as accessors and mutators for each of these fields.  
Whenever a mutator method is called, the Builder sets a boolean flag indicating 
that the field has been set.  All mutators return a reference to 'this', so 
it's possible to chain a series of setter invocations, which makes it really 
easy to construct records in a single statement.  The Builder also has a 
build() method which constructs a record instance using the values that were 
set in the Builder.  When the build() method is invoked, if there are any 
fields that have not been set but have default values as defined in the schema, 
the Builder will set the values of these fields using their defaults.

One nice thing about implementing the builder pattern in a static inner Builder 
class rather than in the record itself is that this enhancement will be 
completely backwards-compatible with existing code.  The record class itself 
would not change, and the public fields would still be there, so existing code 
would still work.  Users would have the option to use the Builder or continue 
constructing records manually.  Eventually the public fields could be phased 
out, and the record would be made immutable.  All changes would have to be done 
through the Builder.

Here is an example of what this might look like:
{code}
// Person.newBuilder() returns a new Person.Builder instance
// All Person.Builder setters return 'this' allowing us to chain set calls 
together for convenience
// Person.Builder.build() returns a Person instance after setting any 
uninitialized values that have defaults
Person me = 
Person.newBuilder().setName("James").setCountry("US").setState("MA").build();

// We still have direct access to Person's members, so the records are 
backwards-compatible
me.state = "CA";

// Person has accessor methods now so that the public fields can be phased out 
later
System.out.println(me.getState());

// No NPE here because the array<Person> field that stores this person's 
friends has been automatically 
// initialized by the Builder to a new java.util.ArrayList<Person> due to a 
@java_class annotation in the IDL
System.out.println(me.getFriends().size());
{code}

What do people think about this approach?  Any other ideas?

--
This message is automatically generated by JIRA.
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

Reply via email to