Programming Tutorials Browser Tutorials Articles Struts Tutorials Hibernate Tutorials

  Tutorial: Reflection vs. code generation

Reflection vs. code generation

Tutorial Details:

Reflection vs. code generation
Reflection vs. code generation
By: By Michael J. Rettig with Martin Fowler
Avoid runtime reflection when marshaling data
ata marshaling (pulling data from an outside source and loading it into a Java object) can utilize the benefits of reflection to create a reusable solution. The problem is simple enough: load data from a file into an object's fields. Now, what if the target Java classes for the data change on a weekly basis? A straightforward solution still works, but you must continually maintain the loading procedures to reflect any changes. To further complicate the situation, the same problem may crosscut the system's breadth. Anyone who has dealt with a large system using XML has encountered this problem. Coding a load procedure is often tedious and subject to frequent updates and rewrites due to changes to the source data or the target Java class. A solution using reflection, which I'll describe here, often requires less coding, and updates itself when changes are made to the target Java class.
Originally, I intended to demonstrate a solution using reflection during runtime for data marshaling. Initially a dynamic, reflection-based program was far more attractive than a simpler approach. Over time, the novelty faded to reveal the complexity and risk of runtime reflection. This article charts this evolution from runtime reflection to active code generation.
From simplicity to complexity
My first solution used a loading class to load the objects' data from a flat file. My source code contained several dozen calls for the next token of a StringTokenizer object. After several refactorings (see Martin Fowler's Refactoring ), my coding logic became straightforward, nearly systematic. The class structure dictated code. My initial solutions showed me that I needed only to account for three basic objects:
Strings
Objects
Arrays of objects
You could map the class's objects to generalized code blocks, as shown in the following table:
Objects mapped to generalized code blocks
Field type
Code block
String
fileIterator.nextString();
Object[]
Vector collector = new Vector();
while(fileIterator.hasMoreDataForArray()){
Object data = initializeObject(fileIterator)collector.add(data);
}
Object[] objArray = new Object[collector.size()];
collector.copyInto(objArray);
Object
initializeObject(fileIterator);
Having coded the solution several times, I knew the solution and the code structure before I wrote any of the code. The difficulty arose from the classes' changing landscape. The class names, compositions, and structures could change at any moment, and any change could force a rewrite. Given these changes, the structure and loading process still remained the same; I still knew the code structure and composition before I wrote the code. I needed a way to translate the coding processes in my head into a reproducible, automated form. Since I am an efficient (i.e., lazy) programmer, I quickly tired of writing nearly identical code. Reflection came to my rescue.
Marshaling usually requires source and target data maps. Maps can take the shape of a schema, DTD (document type definition), file format, and so on. In this case, reflection interprets an object's class definition as the target map for our mapping process. Reflection can duplicate the code's functionality during runtime. So during a required rewrite, I replaced the load procedure with reflection in the same amount of time it would have taken me to do the rewrite.
The load process can be summarized in the following steps:
Interpret: A map decides what you need to construct an object.
If you need to construct other objects first, recurse; repeat step 1.
Request data: To fulfill construction requirements, a call is made to obtain data.
Pull: Data is extracted from source.
Push: Data is stuffed into an object's new instance.
If necessary, repeat step 1.
You need the following classes to fulfill the steps above.
Data classes: Instantiate with the data from the ASCII files. The class definitions provide the map for the data. The following must be true of data classes:
They must contain a constructor that takes all the required arguments to construct the object in a valid state.
They must be composed of objects that the reflective procedure knows how to handle.
Object loader: Uses reflection and the data class as a map to load the data. Makes the data requests.
Load manager: Acts as an intermediary between the object loader and the source data by translating requests for data into a data-specific call. This enables the object loader to be data-source independent. Communicates through its interface and a loadable class object.
Data iterator interface: The load manager and load class objects use this interface to pull the data from its source.
Once you create the supporting classes, you can create and map an object with the following statements:
FooFileIterator iter = new FooFileIterator(fileLocation, log);
LoadManager manager = new FooFileLoadManager(iter);
SubFooObject obj =
(SubFooObject)ReflectiveObjectLoader.initializeInstance(SubFooObject.class, manager,log);
With just this bit of magic, you create a new instance of a SubFooObject containing the file contents.
Limitations
Developers must decide on how best to solve a problem; often, the toughest part is deciding which solution to use. Below are some limitations to keep in mind when considering using reflection for data marshaling:
Do not make a simple problem complex. Reflection can be a hairy beast, so only use it when necessary. Once a developer understands reflection's power, he or she may want to use it to solve every problem. Be careful not to solve a problem with reflection that you can solve more easily, and more quickly, without it (even if it means writing more code). Reflection is as dangerous as it is powerful.
Consider performance. Reflection can be a performance hog because it takes time and memory to discover and manipulate class properties during runtime.
Reassess the solution
As described above, the number one limitation to using runtime reflection is "Do not make a simple problem complex." With reflection, this is unavoidable. Coupling reflection with recursion is one serious headache; reviewing the code is a nightmare; and determining exactly what the code is doing is an intricate process. The only way to truly determine the code's behavior is to step through it, as it would behave at runtime, with sample data. However, to do this for every possible data combination is nearly impossible. Unit testing code helps the situation, but still cannot quell the fears of a developer who sees a possibility for failure. Fortunately, there is an alternative.
The alternative
Given the limitations listed earlier, there are definitely situations where using a reflective load procedure is more trouble than it's worth. Code generation provides a versatile alternative. You can also use reflection to inspect a class and produce code for the load procedure. Andrew Hunt and David Thomas describe two types of code generators in The Pragmatic Programmer .
Passive: A passive code generator requires some human intervention to implement code. Many IDEs provide wizards that take this approach.
Active: Active code generation involves creating code that never has to be modified once it is generated. If a problem arises, the problem should be fixed in the code generator, and not in the generated source files. Ideally, this process should be included in the build process to ensure that the classes never become out of date.
The pros and cons associated with code generators include the following:
Pros:
Simplicity: Generated code is generally more readable and debuggable for developers.
Compile time errors: Reflexive procedures fail more often at runtime than during compilation. For instance, changing the object to be loaded will probably cause the generated loading class to throw a compilation error, but the reflexive procedure won't see any difference until the class is encountered during runtime.
Cons:
Maintenance: With passive code generation, changing the objects to be loaded may require the loading class to be updated or regenerated. If the classes are regenerated, then customizations may be lost.
Take a step back and try again
At this point we can see that using reflection during runtime is not acceptable. Active code generation gives us all the benefits of reflection, but none of its limitations. Reflection will still be used, but only during the code generation process, and not during runtime. The reasons are outlined below:
Less risk. Runtime reflection is definitely high risk, especially as the problem grows more complex.
Unit tests lie, but the compiler doesn't.
Versatility. Generated code can realize all the benefits of runtime reflection, while gaining other benefits elusive in runtime reflection.
Understandability. Even with several more refactorings, the complexity of combining recursion with reflection is inescapable. Generated source code is much easier to explain and understand. The code generation process requires recursion and reflection, but the result is viewable source code, rather than a magically created object.
Write code that writes code
To write a code generator, you must think beyond simply coding a solution to solve a problem. A code generator (and reflection) recreate the mental gymnastics that coding often requires. If you use purely runtime reflection, you are forced to conceptualize the problem at runtime, rather than attack the problem with simple, compilable source code. Code generation lets you view the problem from both perspectives. The code generation process actually converts abstract ideas into concrete source code. Runtime reflection never escapes from abstraction.
The code generation process lets you commit your thought process to code, then generate and compile the code. The compiler lets y


 

Read Tutorial at: Click here to view the tutorial

Rate Tutorial:
Reflection vs. code generation

View Tutorial:
Reflection vs. code generation

Related Tutorials:

Java Tip 71: Use dynamic messaging in Java - JavaWorld - April 1999
Java Tip 71: Use dynamic messaging in Java - JavaWorld - April 1999
 
Code generation using Javadoc - JavaWorld August 2000
Code generation using Javadoc - JavaWorld August 2000
 
Solve your servlet-based presentation problems - JavaWorld November 2000
Solve your servlet-based presentation problems - JavaWorld November 2000
 
Eliminate tedious programming: Recover data with XML and Reflection - JavaWorld November 2000
Eliminate tedious programming: Recover data with XML and Reflection - JavaWorld November 2000
 
Untangle your servlet code with reflection - JavaWorld December 2000
Untangle your servlet code with reflection - JavaWorld December 2000
 
Make an EJB from any Java class with Java Reflection - JavaWorld December 2000
Make an EJB from any Java class with Java Reflection - JavaWorld December 2000
 
Jato: The new kid on the open source block - JavaWorld March 2001
Jato: The new kid on the open source block - JavaWorld March 2001
 
Untangle your servlet code with reflection
Untangle your servlet code with reflection
 
Reflection vs. code generation
Reflection vs. code generation
 
Pick up performance with generational garbage collection
Pick up performance with generational garbage collection
 
Good introduction to JDO
Good introduction to JDO
 
Comparison between the two major JDO architectures
Comparison between the two major JDO architectures
 
Object-relation mapping without the container
If you follow the latest developer buzz then you\\\\\'ve likely heard of IOC (Inversion of Control) containers and AOP (aspect-oriented programming).
 
Practical Reflection: an excerpt from Hardcore Java
Practical Reflection: an excerpt from Hardcore Java In this chapter from Hardcore Java, "Practical Reflection," Robert Simmons Jr. writes: "Reflection is one of the least understood aspects of Java, but also one of the most powerful. Reflection is used i
 
Template-Based Code Generation with Apache Velocity, Part 1
Template-Based Code Generation with Apache Velocity, Part I'm going to discuss template-based code generation, explain basic concepts related to templates and transformations, and demonstrate the huge benefits they can bring in code generation.
 
Extensible Code Generation with Java, Part 1
Extensible Code Generation with Java, Part 1 Code generation is a key new trend in engineering, one that you need to understand well. The reason is simple: today's modern frameworks are extremely code-intensive. Using a code generator to build the code
 
Template-Based Code Generation with Apache Velocity, Part 2
Template-Based Code Generation with Apache Velocity, Part 2 As described in part one of this series, code generation typically uses a template engine to transform some kind of "model" into compilable code, given the formatting specified by a template.
 
Extensible Code Generation with Java
Extensible Code Generation with Java Part 2 In part 1 of this series, we looked at the idea of generated code, which is code written not by hand but by another application. The appeal of generated code is that it can eliminate drudgery (and mistakes) i
 
Mandarax
Mandarax is an open source java class library for deduction rules. It provides an infrastructure for defining, managing and querying rule bases.
 
Reduce code bloat with XDoclet
Reduce code bloat with XDoclet XDoclet can easily be one of the more versatile cross-technology code-generation tools in your Java programming toolbox. Unfortunately, developers often overlook XDoclet's general utility and use it only when it's bundled a
 
Site navigation
 

 

Send your comments, Suggestions or Queries regarding this site at roseindia_net@yahoo.com.

Copyright © 2006. All rights reserved.