79.

Other Obstacles to Factoring Out Code

Factoring out common code is a good practice, but it can't be done in all cases. And, because it's not always an easy matter to actually eliminate the potential for such rogue tiles in a statically typed language like Java, additional considerations can be useful in determining a way around the original design of a language. Two such considerations are aspect-oriented programming.

Generic Types

The simplicity of the Java type system often forces us to choose between taking advantage of static checking and keeping a single point of control for each distinct functional aspect of a program.

For example, suppose we were to write a Visitor over the Tree class hierarchy described in the previous listing. A Visitor is a design pattern that allows for methods to be added to a composite class hierarchy without actually modifying the classes in that hierarchy. A Visitor class will hold methods for each constituent class. These constituent classes will then contain accept() methods that take a Visitor and invoke the appropriate method on it.

We could add Visitors to our Tree hierarchy from the previous listing as follows:

Listing 7-5: Adding Visitors to the Tree Hierarchy

start example
 interface TreeVisitor {   public Object forLeaf(Leaf that);   public Object forBranch(Branch that); } // in class Tree public abstract Object accept(TreeVisitor that); // in class Leaf public Object accept(TreeVisitor that) {   return that.forLeaf(this); } // in class Branch public Object accept(TreeVisitor that) {   return that.forBranch(this); } 
end example

Notice that the return types of the TreeVisitor methods are all necessarily of the type Object. We can't narrow this return type without limiting the generality of interface TreeVisitor. So, we're left with two options:

  1. Use the same TreeVisitor interface for all occasions and insert casts as appropriate with each Visitor method invocation. That's not just a lot of work, it effectively circumvents the static type system and it makes it much more likely that we will experience a ClassCastException at runtime.

  2. Construct a separate TreeVisitor interface with separate accept() methods for each return type we want to use. But that's exactly how rogue tiles are formed.

To be sure, any statically typed language will limit expressiveness and will, to some degree, impair our efforts to prevent rogue tiles. But Java doesn't have to be as bad as it is in this regard.

Note 

Any statically typed language will limit expressiveness and will, to some degree, impair our efforts to prevent rogue tiles.

A more powerful type system, such as that implemented by the Sun JSR-14 prototype compiler, would alleviate this issue significantly. In the JSR-14 compiler, the Java type system is augmented with what are known as generic types, or parametric polymorphism. Parametric polymorphism allows us to add type parameters-declared in angle brackets in the class header-that can be instantiated when a particular instance of the class is made.

Note 

Parametric polymorphism allows us to add type parameters-declared in angle brackets in the class header-that can be instantiated when a particular instance of the class is made.

For example, in our Tree hierarchy, it would make sense to parameterize the contained value of class Tree. Then we could instantiate that value type in different ways depending on the circumstances.

Also, we may want to parameterize our Visitor class by the return type of the for-() methods. That way, when we construct a concrete Visitor class for a particular circumstance, the Visitor can extend an instantiation of the abstract Visitor class with the appropriate return type.

Listing 7-6: A Parametric Version of Interface TreeVisitor

start example
 interface TreeVisitor<ReturnType> {   public ReturnType forLeaf(Leaf that);   public ReturnType forBranch(Branch that); } 
end example

One limitation of JSR-14 is that it does not allow run-time type operations (such as casts, instanceof tests, etc.) on generic types. At the JavaPLT laboratory at Rice University, we have extended the language implemented by JSR-14 to include full support for these run-time type operations. The extended language is referred to as NextGen. Because NextGen is implemented in a manner that is compatible with existing compiled binaries, we are hopeful that, eventually, it will be incorporated into the Java language.

This is just a taste of what can be done with generic types. For more information on them, see the References chapter of this book.

Aspect-Oriented Programming

Static typing isn't the only way that a language like Java can prevent us from eliminating rogue tiles. Other constraints on the language, such as the requirement that all checked exceptions be caught or declared to be thrown, force us to include code in our methods that simply can't be factored out.

One mechanism for handling such duplicated code is aspect-oriented programming. In aspect-oriented programming, a development tool systematically inserts code you specify into methods to maintain global properties, allowing you to organize a program according to various aspects rather than simply by classes or functions. These aspects correspond to global properties of the program, such as the way that checked exceptions are handled in methods or the way that fields are assigned in constructors.

There are several existing tools for adding aspect-oriented programming to Java. One popular tool is AspectJ. For more information, refer to the References chapter.



Bug Patterns in Java
Bug Patterns In Java
ISBN: 1590590619
EAN: 2147483647
Year: N/A
Pages: 95
Authors: Eric Allen

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net