|
|
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
Page 3 of 5

The general rule of orthogonality is to avoid references to specific concrete types representing other aspects (dimensions) of the program. This enables you to write generic code that will work the same way for all possible instances. Such code can still reference properties of instances, as long as they are part of the interface of the type defining the dimension.
For instance, in Log4j the abstract type Layout defines the method ignoresThrowable(). This method returns a boolean indicating whether the layout can render exception stack traces or not. When an appender uses
a layout, it would be perfectly fine to write conditional code on ignoresThrowable(). For instance, a file appender could print exception stack traces on System.err when using a layout that could not handle exceptions.
In a similar manner, a Layout implementation could refer to a particular Level when rendering logging events. For instance, if the log level was Level.ERROR, an HTML-based layout implementation could wrap the log message in tags rendering it in red. Again, the point is that Level.ERROR is defined by Level, the type representing the dimension.
You should, however, avoid references to specific implementation classes for other dimensions. If an appender uses a layout then there is no need to know what kind of layout it is. Figure 4 illustrates good and bad references.

Several patterns and frameworks make it easier to avoid dependencies to implementation types, including dependency injection and the service locator pattern.
Overall, Log4j is a good example of the use of orthogonality. However, some code in Log4j violates this principle.
Log4j contains an appender called JDBCAppender, which is used to log to a relational database. Given the scalability and popularity of relational database, and the fact
that this makes log events easily searchable (with SQL queries), JDBCAppender is an important use case.
JDBCAppender is intended to address the problem of logging to a relational database by turning log events into SQL INSERT statements. It solves this problem by using a PatternLayout.
PatternLayout uses templating to give the user maximum flexibility to configure the strings generated from log events. The template is
defined as a string, and the variables used in the template are instantiated from log events at runtime, as shown in Listing
2.
String pattern =
"%p [@ %d{dd MMM yyyy HH:mm:ss} in %t] %m%n";
Layout layout =
new org.apache.log4j.PatternLayout(pattern);
appender.setLayout(layout);
JDBCAppender uses a PatternLayout with a pattern that defines the SQL INSERT statement. In particular, the following code can be used to set the SQL statement used:
public void setSql(String s) {
sqlStatement = s;
if (getLayout() == null) {
this.setLayout(new PatternLayout(s));
}
else {
((PatternLayout)getLayout()).setConversionPattern(s);
}
}
Built into this code is the implicit assumption that the layout, if set before using the setLayout(Layout) method defined in Appender, is in fact an instance of PatternLayout. In terms of orthogonality, this means that suddenly a lot of points in the 3D cube that use JDBCAppender with layouts other than PatternLayout do not represent valid system configurations anymore! That is, any attempts to set the SQL string with a different layout
would result in a runtime (class cast) exception.