A couple of days ago, I was writing a mini parser to help me pragmatically analyse some data on a project and I had a little hiccough. Several different ‘Types” of information were represented using the same very simple syntax – essentially tokens joined with AND (‘+’) and OR (‘,’) operators.
Simple enough (I thought), so I wrote a tokeniser (implementing the Iterator & Iterable interfaces) and an abstract base class with a simple parser. I added an abstract static factory method which could be overriden to provide an instance of the appropriate type. Then, derive a class for each type of information, each overriding the static factory method. Then the application can call the parser using the static method via the required derived class. This is illustrated in the code below (with irrelevant stuff removed): –
public abstract class AbstractBase extends Expression
{
public abstract static Expression find( String name );
public static Expression parse( String source )
{
Expression expr = null;
String token = null;
...
Expression anElement = find( token );
...
return expr;
}
}
public class TypeOne extends AbstractBase
{
private static Map<String,TypeOne> instances = new TreeMap<String,TypeOne>();
public static Expression find( String name )
{
...
}
}
public class TypeTwo extends AbstractBase
{
private static Map<String,TypeTwo> instances = new TreeMap<String,TypeTwo>();
public static Expression find( String name )
{
...
}
}
public class SampleLoader
{
public void loadData( File path )
{
...
TypeOne.parse( someText );
...
TypeTwo.parse( someText );
...
}
}
This looks all very simple – but what you can’t see here is that my IDE (Ecliipse) complained about the abstract ‘find’ method – “The abstract method find in type AbstractBase can only set a visibility modifier, one of public or protected”. Of course, I should have clicked then, but instead I modified it to: –
public static Expression find( String name )
{
return null;
}
But, on running the application I was surprised to find that the find(...)
method kept returning null
. A quick search on Google answered the immediate question – “why?” Take a look at these Sun and Java Ranch articles for a clear and sensible explanation. Obvious, when you think about it!
Anyway, thanks for the explanation – but that doesn’t solve the problem? This is where Patterns help. Here are some of the possible solutions: –
- Pass in a flag to indicate the type of object to create. This would cause additional complexity in the parser and makes unnecessary work if later another type is to be parsed by the parser. It also introduces a possible source of errors if there is a mismatch between the flag and the type of object coded for it.
- Make the factory method non-static and pass in an instance of the object. While that would work it means you are abusing the meaning of the object by using it to create it’s siblings. It’s just wrong!
- Pass in the class of the type to be created. A good solution but it doesn’t work well if you want to share instances of the objects or if the objects are from a fixed set of values (as these are).
- Use a separate Factory class – i.e. with the Abstract Factory design pattern. A nice pattern based solution.
I decided to use the 4th option – the Abstract Factory design pattern – this is the simplest refactoring:
// Add a new interface (the Abstract Factory interface).
public interface TypeFactory
{
Expression find( String name );
}
public class SampleLoader
{
// Add two anonymous implementations...
private static TypeFactory typeOneFactory = new TypeFactory()
{
public Expression find( String name )
{
return TypeOne.find( name );
}
};
private static TypeFactory typeTwoFactory = new TypeFactory()
{
public Expression find( String name )
{
return TypeTwo.find( name );
}
};
// Pass the factories to the Parser.
public void loadData( File path )
{
...
parse( someText, typeOneFactory );
...
parse( someText, typeOneFactory );
...
}
// Move the Parser into the loading class - where it belongs.
public Expression parse( String source, TypeFactory factory )
{
Expression expr = null;
String token = null;
...
Expression anElement = factory.find( token );
...
return expr;
}
}
public abstract class AbstractBase extends Expression
{
public abstract static Expression find( String name );
// Parser moved to the SampleLoader class.
}
// TypeOne and TypeTwo are unchanged.
This approach now works perfectly and ensures that the loading and parsing code is kept separate from the internal representation – thus making the code easier to maintain or reuse.
For more information have a look at this Wikipedia article – or a good book on Design Patterns such as the succinctly titled Design Patterns: AND Applying UML and Patterns, an Introduction to Object-Oriented Analysis and Design and Iterative Development: Elements of Reusable Object-oriented Software.
Yes, you are right but i don’t absolutely agree. I think it’s only partial right. But post is god i think.