Category: design patterns Gene De Lisa @ 12:45 pm —
1 Star2 Stars3 Stars4 Stars5 Stars (2 votes, average: 5 out of 5)
Loading ... Loading ...

Let say we have some Pet domain classes. Pet is an interface which is implemented by Cat and Bird. Nothing more complicated than that for now.

Of course you can always say:

Pet giacomo = new Cat();

This does indeed work. But let’s say you just want a Pet and you don’t really care what kind it is as long as it behaves like a Pet (implements the Pet interface). In this code you are being one of those micromanaging pointy haired jerk bosses you hate. Not only are you saying you want a Pet you’re saying exactly how to get one. Isn’t it better when the boss says “I need a Pet. You’re smart. Just get me one and do it any way you think is best”.

So, let’s model you the Pet procurer as a class. We make a class with one method: the method that returns a Pet. We could implement it by just new-ing a concrete implementation.

public static getPet() {
   return new Cat();
}

But let’s be a little geekier. We can instantiate an object from a fully qualified class name with the java.lang.Class class.

public class PetManager {
	private static String petClassName = "domain.Cat";

	public static Pet getPet() {
		Pet p = null;
		try {
			Class c = Class.forName(petClassName);
			p = (Pet) c.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return p;
	}
}

And of course a JUnit test.

package simple;

import domain.Pet;
import junit.framework.TestCase;

public class PetManagerTest extends TestCase {

	public void testGetPet() {
		Pet fluffy = PetManager.getPet();
		assertNotNull(fluffy);
		System.out.println(fluffy);

		Pet momo = PetManager.getPet();
		assertNotNull(momo);
		assertNotSame(momo, fluffy);
		System.out.println(momo);
	}
}

I’m printing to stdout just to see what class I get. We do indeed get a Pet instance returned and each time they are new objects.

Why write one line when you can write a zillion right? No, that’s not the point.
Since it creates Pets why not call it a Factory?
There is a bunch of cool things we can do with this Pet Factory. Since we are creating instances from a string, that string could come from a properties file, be specified in a deployment descriptor, or even injected by a container like Spring. We could also cache instances. And more interestingly, return an implementation more suited to the client’s preferences. I personally like Cats better than cockroaches, but you might disagree.

Let’s work on that preference thing. Let say we want to get an instance based on a common name for that Pet class. Specify “fido” and get a dog, “fluffy” and get a cat etc. (Ok, so I had a bird named rover. never mind that). A first step would be to set up a Map between the pet names (e.g. “fluffy”) and the java.lang.Class of the implementation.

public class PetManager {
       static Map<String,Class?> handlers = new HashMap<String><Class?>();

        static {
		try {
			register("fluffy", Class.forName("domain.Cat"));
			register("tweety", Class.forName("domain.Bird"));
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	public static void register(String key, Class c) {
		handlers.put(key, c);
	}

	public static Pet getPet(String key) {
		Pet p = null;
		try {
			Class c = handlers.get(key);
			if (c == null) {
				System.err.println("No handler for " + key);
				return null;
			}
			p = (Pet) c.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return p;
	}
}

This PetManager factory handles exactly two kinds of pets. We could have it handle many more if something simply calls the register method. That’s why it’s public. We could have each pet implementation register themselves in their static initialization block - which is called only once (per jvm) when the class is loaded.

public class Cat implements Pet {
    static {
        PetManager.register("fluffy", Cat.class);
    }
}

We can then get rid of the static initialization block in the factory, putting the responsibility for registering up to the domain classes.

There’s one problem. Ok, one to start with.
Let’s say the client does this:

Pet giacomo = PetManager.getPet("fluffy");

Notice that there is no mention of Cat here? Since the client is not saying Cat anyplace the classloader will not look for and load the Cat bytecode. We don’t usually think about this because it happens automatically when we use a class’ name.

So how do you make the classloader load a class into the JVM? We’ve already done it above in a different context.

try {
	Class.forName("domain.Cat");
} catch (ClassNotFoundException e) {
	e.printStackTrace();
}
Pet giacomo = PetManager.getPet("fluffy");

Try it with and without and see what you get.

Now let’s make it a bit more geekier. Let’s configure the petname/classname in a properties file. The first time we ask for a petname we try to find the matching class and if found cache it in the handlers Map. Then create and instance and return it. If the determinePetClass method throws and exception just hot potato it to the client.

public static Pet getPet(String key) {
	Pet p = null;
	Class c = null;
	try {
		if (handlers.containsKey(key)) {
			c = handlers.get(key);
		} else {
			c = determinePetClass(key);
			handlers.put(key, c);
		}
		p = (Pet) c.newInstance();
	} catch (InstantiationException e) {
		e.printStackTrace();
	} catch (IllegalAccessException e) {
		e.printStackTrace();
	}
	return p;
}

The properties file is something like:

fluffy=domain.Cat
tweety=domain.Bird

To determine the pet class we need to load in a properties file.

If you want to say something like:

java -Dfido=domain.Doggie Client

We first create the Properties object using the System properties. Remember that the first guy in the properties pool wins.
Then we can read the actual properties file from the classpath using one or another of the classloader’s methods. I’ll show you both.
Why not just load from a file? You’d can but you’d have to specify the entire path to the properties file. A bit of a maintenance pain.

If the pet name is not found I just throw an IllegalArgumentException. Maybe you want to use something more specific like WhatTheHeckKindOfPetIsThatException.

private static Class determinePetClass(String key) {
	Class c = null;
	// in case its specified via -D
	Properties p = new Properties(System.getProperties());
	try {
		// look on the classpath with the packagename as a path
		URL url = ClassLoader
				.getSystemResource("fromproperties/petconfig.properties");
		if (url != null) {
			p.load(url.openStream());
			System.out.println("loaded from classpath");
		}
		// and/or
		p.load(PetManager.class.getClassLoader().getResourceAsStream(
				"fromproperties/petconfig.properties"));
			String petClassName = p.getProperty(key);
		if (petClassName == null) {
			throw new IllegalArgumentException(key + " unknown");
		}
		c = Class.forName(petClassName);
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	}
	return c;
}

So there. We can get what we want through the PetManager factory by specifying a name. And we can change the name in the properties file for different situations. Maybe right now fluffy=domain.Kitten and later fluffy=domain.VoraciousLivingRoomLion.

“But I hate cats” you say. And what does this have to do with any “real” programming?
Have you ever seen code like the following?

Class.forName("org.hsqldb.jdbcDriver");
Connection  con =
            DriverManager.getConnection("jdbc:hsqldb:mem:somedatabase");

JDBC drivers register themselves with the DriverManager (like our PetManager). The getConnection method is like our getPet method. The JDBC URL determines which driver is in the driver’s seat.

Now you know why you do that thing you gotta do in JDBC.

This of course is simple and great if it’s 1995 or for simple toys. You see, in this client code you are hardcoding the database type and the actual db you are going to. What if it’s different between development,qa/staging and production. Gotta change the code.

There are better ways to get a database connection.
JNDI lookup was the first replacement where all the db specific stuff was shoved onto the Weblogic/Websphere/whatever admin console.

Hibernate put all this in an XML file and so does JPA and Spring.

You can download a maven-ized eclipse project (or is that an eclipsed maven project?)
here.

Sorry, no comments yet.

Write Your Comment

Comment Guidelines: Basic XHTML is allowed (a href, strong, em, code). All line breaks and paragraphs will be generated automatically.

You should have a name, right? 
Your email address, I promised I won't tell it to anyone. 
If you have a web site or blog, you can type the URL right here. 
This is where you type your comments. 
Remember my information for the next time I visit.