template with the binding method.
Listing 4.16 shows the template used by the tier generator to build the data structure classes.
public class <%= class_name %> {
<% fields.each { |field| %>
protected String _<%= field %>;<% } %>
public <%= class_name %>() { <% fields.each { |field| %>
_<%= field %> = new String();<% } %> }
<% fields.each { |field| %>
public String get<%= field.capitalize %>() { return _<%= field %>; } public void set<%= field.capitalize %>( String value ) { _<%= field %> = value; }
<% } %>
}
4.5.2 Developing the generator
Developing a tier generator is much like building any of the other generators in this chapter. We show the steps in figure 4.21, but you should follow your own path; these steps are merely a starting point.
Let’s examine these steps in detail:
• Build the test code—One of the great things about building a code generator is that you start at the end. The first step is to prototype what you would like to generate by building the output code by hand. In most cases you already have the code you want to generate in hand because it’s already part of the application.
Listing 4.16 field_class.template.java
Build Test Code
Design Generator
Develop Templates From Test Code
Develop Input Parser
Develop Output Code Builder
Figure 4.21
The design and implementation steps for developing a tier generator
• Design the generator—Once you have the output, you need to determine how you are going to build a generator that will create the test code as output. The most important job is to specify the data requirements. What is the smallest possible collection of knowledge that you need to build the output class? Once you have the data requirements, you will want to design the input definition file format. • Develop the input parser—The first implementation step is to build the definition
file parser. In the case of XML, this means writing the Rexml access code to read in the XML and to store it in some internal structures that are easy for the tem- plates to handle.
• Develop the templates from the test code—Once the input is coming into the gener- ator, you must create the templates that will build the output files. The easiest way to do this is to take the test code you started as the starting point for the template. • Develop the output code builder—The final step is to write the glue code that runs the templates on the input specification from the definition file and builds the output files. At the end of the development, you should be able to run the gener- ator on your definition file and have the output match the test code that you cre- ated in the beginning of the process.
4.6
GENERATING
FOR
VARIOUS
LANGUAGES
Each language creates unique issues for code generation. Each has a unique coding style; in addition to this implicit style, most companies have coding guidelines that enforce additional style requirements. Your generated code should follow this style, just as if you had written the code by hand. This is one of the most important reasons to use templates when building code. Templates allow you the freedom to add white space and position generated code as if you were writing it yourself.
A common complaint about generated code is that the code is not visually appeal- ing. Ugly code conveys a lack of care to the reader and disrespect for the output lan- guage and the production code. You should strive to make your templates and their resulting code as clean as if you had written every line yourself. In this section, we explore each of the popular languages and offer advice on using generation techniques and avoiding the common pitfalls.
4.6.1 C
For C, you’ll need to keep these considerations in mind:
• Only externally visible functions and constants should be visible in generated header files.
• Internal functions and constants generated within the implementation files should be marked as static.
GENERATINGFORVARIOUSLANGUAGES 95
• If you have a development environment that allows for code collapsing while edit- ing, be sure to develop the code in a way that matches the code-collapsing standard. • Set your compiler to the highest warning level and generate code that compiles
without warnings.
• Consider bundling all of the generated code into a single library and then importing that as a component into the application.
4.6.2 C++
In addition to the guidelines for C generation, you should take into account these factors when generating C++:
• Consider using C++ namespaces to contain the generated code in a logical grouping. • Consider using a pattern where you expose an abstract base class with only public members. This base class is exported in the header. Then, use this abstract class as the basis for an implementation class, which is implemented only in the .cpp file. A factory method can be exported that creates a new instance of the derived class. This will ensure that recompiles will not be necessary when private methods or variables are changed by the generator.
4.6.3 C#
C# appears to have been designed for generation. You should use the inherent language features to your advantage:
• Use the #region pragma to allow for code collapsing in the editor.
• Consider packaging all of the generated code as an assembly to reduce compile times and to create logical boundaries between system components.
• Make use of the XML markup capabilities to allow for easy documentation generation.
4.6.4 Java
Here are some suggestions when generating Java:
• For generator input, use the JavaDoc standard when appropriate and the Doclet API for access to your JavaDoc comments.
• For generator output, be sure to include JavaDoc markup in your templates to ensure that you can use the JavaDoc documentation generation system.
• Consider generating interfaces for each of your output classes. Then, generate concrete classes for these interfaces. This will reduce compile and link times as well as loosen the coupling between classes.
4.6.5 Perl
Here are some suggestions when generating Perl:
• You should always include use strict in the code you generate and run perl with the –w option.
• Generate POD documentation along with the code.
• Be sure to use package containment and explicit exports to ensure that your functions don’t bleed into the main namespace.
4.6.6 SQL
Keep these suggestions in mind when generating SQL:
• You should spend the time to make sure that the output SQL is marked up with the portions of the SQL code that diverge from the SQL standard to use vendor- specific options.
• You should separate the schema, any stored procedures, and any test data into separate files.
• When generating SQL within another language (e.g., Java executing SQL using strings and JDBC), be sure to use the argument replacement syntax within the driver. In Java this means using the PreparedStatement class and the ques- tion mark replacement syntax.
4.7
SUMMARY
This chapter provided you with a set of starting points that you can use to build your own generators. From this point on, we will be taking these generators and building on them to show how they can be used to solve a number of common software engineer- ing problems. In the next chapter, we’ll begin building generators for specific types of uses, starting with generators for user interface elements such as web forms.