Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Java tip: Orthogonality by example

The principles of modular and maintainable design in Log4j

  • Print
  • Feedback

Page 2 of 5

Figure 1. An orthogonal system in three dimensions

Listing 1 is a typical code snippet implementing Log4j:

Listing 1. A Log4j implementation example

// setup logging !
Logger logger = Logger.getLogger("Foo");        
Appender appender = new ConsoleAppender();
Layout layout = new org.apache.log4j.TTCCLayout()
appender.setLayout(layout);
logger.addAppender(appender);
logger.setLevel(Level.INFO);
// start logging !
logger.warn("Hello World");

What I want you to notice about this code is that it is orthogonal: you could change the appender, layout, or level aspect without breaking the code, which would remain completely functional. In an orthogonal design, each point in the given space of the program is a valid system configuration. No constraint is allowed to restrict which points in the space of possible configurations are valid or not.

Orthogonality is a powerful concept because it enables us to establish a relatively simple mental model for complex application use cases. In particular, we can focus on one dimension while ignoring other aspects.

Testing is a common and familiar scenario where orthogonality is useful. We can test the functionality of log levels using a suitable fixed pair of an appender and a layout. Orthogonality ensures us that there will be no surprises: log levels will work the same way with any given combination of appender and layout. Not only is this convenient (there is less work to do) but it is also necessary, because it would be impossible to test log levels with every known combination of appender and layout. This is especially true given that Log4j, like many software tools and utilities, is designed to be extended by third parties.

The reduction in complexity that orthogonality brings to software programs is similar to how dimensions are used in geometry, where the complicated movement of points in an n-dimensional space is broken down to the relatively simple manipulation of vectors. The entire field of linear algebra is based on this powerful idea.

Designing and coding for orthogonality

If you are now wondering how to design and code orthogonality into your programs, then you are in the right place. The key idea is to use abstraction. Each dimension of an orthogonal system addresses one particular aspect of the program. Such a dimension will usually be represented by a type (class, interface, or enumeration). The most common solution is to use an abstract type (interface or abstract class). Each of these types represents a dimension, while the type instance represents the points within the given dimension. Because abstract types can not be directly instantiated, concrete classes are also needed.

Figure 2. Inside the Appender dimension

In some cases we can do without them. For instance, we don't need concrete classes when the type is just a markup, and doesn't encapsulate behavior. Then we can just instantiate the type representing the dimension itself, and often predefine a fixed set of instances, either by using static variables, or by using an explicit enumeration type. In Listing 1 this rule would apply to the "level" dimension.

  • Print
  • Feedback

Resources