Breaking the Last Dependency Blog

Version 2

    {cs.r.title}



              
                        

    Contents
    Setting the Stage
    Factory Refresher
    Let's Break that Last Dependency
    Taking It All the Way
    Summary
    Complete Code

    As Head First Design Patterns was about to go to press, Erich Gamma sent us a note suggesting that in the factory pattern chapter we should break the last dependency and show how to write code that does away with concrete classes completely. He was right on--that was the next logical step, but because of size and time constraints, sadly that topic just didn't get into the book. But on java.net, we have no such constraints, so let's tackle this topic now.

    Setting the Stage

    First let's work through what "breaking the last dependency" means, how it relates to the factory pattern, and why you should care. First, all of the factory patterns "encapsulate" the instantiation of concrete classes and help to minimize (as well as localize) the dependencies your code has on those concrete classes. What does that mean? Consider the following code:

     public class StampedeSimulator { Actor actor = null; public void addActor(String type) { if (type.equals("buffalo")) { actor = new Buffalo(); } else if (type.equals("horse")) { actor = new Horse(); } else if (type.equals("cowboy")) { actor = new Cowboy(); } else if (type.equals("cowgirl")) { actor = new Cowgirl(); } // rest of simulator here } }
    

    This code is tied to four different concrete classes (Buffalo, Horse, Cowboy, andCowgirl) and, as a result, creates a dependency between your code and these concrete classes. Why is that a bad thing? Well, whenever you add a new type (say, aCoyote) or reconfigure the concrete classes (say you want to use the FastHorse class instead of the genericHorse class) you'll have to rework this code (read: high maintenance). Keep in mind you might have similar concrete instantiations sprinkled all around your code, and so you're going to have to change this code in multiple places (in other words, you're setting yourself up for lots of bugs). Note that we could also clean this code up and make it more type-safe by using Java 5's enumerations instead of matching strings, but since everyone isn't on Java 5 yet (like you Mac users), we'll leave that exercise for another time.

    OOCode

    Now, what if we had a way to minimize the dependency on the concrete classes? This would make your life easier by reducing the amount of code you have to maintain, and give you time for all of that other stuff you'd rather be doing anyway. That's where factories come in.

    Factory Refresher

    There are several kinds of factories, which you can look up in any patterns book. For the purposes of demonstration, let's take a look at a Static Factory, which consists of a class that provides a static method to handle the instantiation of an object. To implement this, we put all of the instantiation code into a factory, ActorFactory, and replace this code in the StampedeSimulator with code that uses the factory to create the objects:

     public class ActorFactory { static public Actor createBuffalo() { return new Buffalo(); } static public Actor createHorse() { return new Horse(); } static public Actor createCowboy() { return new Cowboy(); } static public Actor createCowgirl() { return new Cowgirl(); } }
    

    And we can alter our StampedeSimulator to look like this:

     public class StampedeSimulator { Actor actor = null; public void addActor(String type) { if (type.equals("buffalo")) { actor = ActorFactory.createBuffalo(); } else if (type.equals("horse")) { actor = ActorFactory.createHorse(); } else if (type.equals("cowboy")) { actor = ActorFactory.createCowboy(); } else if (type.equals("cowgirl")) { actor = ActorFactory.createCowgirl(); } // rest of stampede simulator here } }
    

    Only this is a little unsatisfying, because we now we havetwo if-then-elseclauses! So let's parameterize the factory to take a string indicating what kind of object to instantiate:

     public class ActorFactory { static public Actor createActor(String type) { if (type.equals("buffalo")) { return new Buffalo(); } else if (type.equals("horse")) { return new Horse(); } else if (type.equals("cowboy")) { return new Cowboy(); } else if (type.equals("cowgirl")) { return new Cowgirl(); } else { return null; } } } public class StampedeSimulator { Actor actor = null; public void addActor(String type) { actor = ActorFactory.createActor(type); // rest of stampede simulator here } }
    

    There we go; now we have a nice separation between the instantiation of the concrete classes and our main code. Notice that the return type of the method in the factory is an interface (or it can be an abstract class), which enforces the fact that your client doesn't have to know about the concrete classes. So, by writing your client code to use the interface, you keep it decoupled from your concrete classes. The Static Factory takes care of creating the objects you want, and your client code doesn't have to worry about it. Now, if you need to make changes, you go to one place in the code where those instantiations are "encapsulated."

    So this encapsulation of our concrete classes into the factory is a good thing--we've decoupled the main code from the concrete classes--but the factory itself still depends on concrete classes, and if we need to change those classes in the factory, it means going into the code, making the changes, and recompiling. So we're not where we want to be yet, but we'll get there by removing all such dependencies in our code.

    Before we go on, we should point out that the Static Factory is usually considered an idiom rather than a true design pattern, but it is in such common use that people usually use the word "factory" to apply to this method of creating objects. In any case, you can use the techniques we're about to go over with Static Factory or any of the true factory patterns (like the Factory Method or Abstract Factory patterns).

    Let's Break that Last Dependency

    We've decoupled the main portion of the application from the concrete classes, but the Static Factory,ActorFactory, is still tightly bound to each concrete class. In addition, that's a pretty uglyif-then-else statement inside the factory. How can we improve this and remove these last dependencies?

    One technique is to use Java's Class.forName(). TheforName() class method allows you to load a class dynamically by passing it a string representing the package and name of the class. Once you've got the class, you just need to instantiate a new instance of it, and return it. Let's see how this works:

     class ActorFactory { static public Actor createActor(String type) { Actor actor = null; Class actorClass = null; try { actorClass = Class.forName(type); } catch (ClassNotFoundException e) { System.out.println("Error: class " + type + " not found."); e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } if (actorClass != null) { try { actor = (Actor) actorClass.newInstance(); } catch (Exception e) { e.printStackTrace(); } } return actor; } }
    

    This code further decouples your application from the concrete classes, because now you can pass in any class name (or at least, any class that implements the Actor interface) to the factory, and you'll get an instance of that class. The price we pay for this decoupling is that we have to do checks all along the way: first, to make sure that the string we pass in representing the class actually exists, and then to make sure that you can actually make an instance of that class. We're a bit lazy here, and we just print out a stacktrace of the exception that occurs if we can't load the class or instantiate an object; in a real-world application, obviously you'd have to do more. We're also trading the flexibility for less control over static type checking (but good testing tends to make this a non-issue). You should think through the subtleties though; for instance, it would be perfectly legal for us to load the Actor class, but we can't actually instantiate an object from the Actor class, because Actor is an interface.

    Once we've made this change to ActorFactory, we just have to make a small change to the application code that passes the string representing the type of actor we want. For example:

     simulator.addActor("headfirst.factory.simulator.Buffalo"); simulator.addActor("headfirst.factory.simulator.Horse"); simulator.addActor("headfirst.factory.simulator.Cowboy"); simulator.addActor("headfirst.factory.simulator.Cowgirl");
    

    That's it! We can compile and run this code and we get exactly the same result as before: one of each type of actor is instantiated.

    Now, when we want to change the actors for the stampede simulator (for instance, when we're making a movie with animated actors instead of real actors), all we have to do is change the string representing the type of actor we pass toaddActor() (which in turn gets passed to theActorFactory). We don't have to change any code in theActorFactory or StampedeSimulator classes at all.

    Taking It All the Way

    This is an improvement, but the code is still coupled to the specific types of actors: we still have to specify the name of theActor type we want in our code and pass it toaddActor(), which means we have to recompile when we want to change actors. Is there any way to get the actor types out of there altogether, so there's no code that depends on the type of the actor we want?

    One way we can remove all code that depends on a specific type is to specify the types of actors we want in a properties file and load them at runtime. Then we'll have no code that depends on the type of the actor.

    To do this, we change how we specify the actor type. Instead of hardcoding the actor type, we'll replace this with code to load the types from a properties file called actor.properties. The properties file has one line for each actor type you need, and looks like this:

     buffalo = headfirst.factory.simulator.Buffalo horse = headfirst.factory.simulator.Horse cowboy = headfirst.factory.simulator.Cowboy cowgirl = headfirst.factory.simulator.Cowgirl
    

    This is the standard format for a Java properties file: the name of the property (e.g., buffalo), then =, then the value of the property. Now, instead of passing the fully qualified pathname of the type of actor tocreateActor(), we just pass in a string representing the type (like we did in the first version of the code), which should match a property in the properties file:

     simulator.addActor("buffalo"); simulator.addActor("horse"); simulator.addActor("cowboy"); simulator.addActor("cowgirl");
    

    We also modify the ActorFactory method,createActor(), to load the properties from the properties file into a Properties instance. Then we use the type passed into createActor() (for instance, "buffalo") to get the value of that property--the fully qualified type of the actor--and use it to instantiate the actor object desired:

     static public Actor createActor(String type) { Class actorClass = null; Actor actor = null; String actorType = null; Properties properties = new Properties(); try { properties.load(new FileInputStream("simulator.properties")); } catch (IOException e) { System.out.println("Error: couldn't read from the simulator.properties file." + e.getMessage()); } actorType = properties.getProperty(type); if (actorType == null || actorType.equals("")) { System.out.println("Error loading actor type for type: " + type); } try { actorClass = Class.forName(actorType); } catch (ClassNotFoundException e) { System.out.println("Error: class " + actorType + " not found!"); System.out.println(e.getMessage()); } catch (Exception e) { e.printStackTrace(); System.out.println(e.getMessage()); } if (actorClass != null) { try { actor = (Actor) actorClass.newInstance(); } catch (Exception e) { e.printStackTrace(); System.out.println(e.getMessage()); } } return actor; }
    

    You could, of course, add properties to specify how many of each type to add to the simulator, as well.

    Now you have no need to specify an actor concrete class anywhere in your code. You're completely decoupled!

    Summary

    The intent of the various factory patterns is to reduce dependencies on concrete classes. Let's step through our progress and see how we removed the last dependency. First, we pulled the code to instantiate objects out of our main code and put it into a factory class. Then, we improved on this by loading the concrete classes and instantiating them dynamically, based on the path and class name passed to the factory. Just make sure each class passed in implements the interface returned by the factory. Last, we broke the final dependency by loading the types we want to use in the simulator from the properties file. This eliminated dependencies to concrete classes in our code altogether.

    Remember, when you reduce dependencies, you make your code more flexible and easier to maintain and extend.

    Complete Code

    If you want to try this program, you can copy the code below to one file, StampedeSimulatorTestDrive.java:

     package headfirst.factory.simulator; import java.io.FileInputStream; import java.io.IOException; import java.util.Properties; public class StampedeSimulatorTestDrive { public static void main(String[] args) { System.out.println("Stampede Test Drive"); StampedeSimulator simulator = new StampedeSimulator(); simulator.addActor("buffalo"); simulator.addActor("horse"); simulator.addActor("cowboy"); simulator.addActor("cowgirl"); } } class StampedeSimulator { public void addActor(String type) { Actor actor = null; actor = ActorFactory.createActor(type); actor.display(); // rest of stampede simulator here } } class ActorFactory { static public Actor createActor(String type) { Class actorClass = null; Actor actor = null; String actorType = null; Properties properties = new Properties(); try { properties.load(new FileInputStream("simulator.properties")); } catch (IOException e) { System.out.println("Error: couldn't read from the simulator.properties file." + e.getMessage()); } actorType = properties.getProperty(type); if (actorType == null || actorType.equals("")) { System.out.println("Error loading actor type for type: " + type); } try { actorClass = Class.forName(actorType); } catch (ClassNotFoundException e) { System.out.println("Error: class " + actorType + " not found!"); System.out.println(e.getMessage()); } catch (Exception e) { e.printStackTrace(); System.out.println(e.getMessage()); } if (actorClass != null) { try { actor = (Actor) actorClass.newInstance(); } catch (Exception e) { e.printStackTrace(); System.out.println(e.getMessage()); } } return actor; } } interface Actor { public void display(); } class Buffalo implements Actor { public void display() { System.out.println("I'm a Buffalo"); } } class Horse implements Actor { public void display() { System.out.println("I'm a Horse"); } } class Cowboy implements Actor { public void display() { System.out.println("I'm a Cowboy"); } } class Cowgirl implements Actor { public void display() { System.out.println("I'm a Cowgirl"); } }
    

    Make sure and save this file in the directorysrc/headfirst/factory/simulator. (If you've already downloaded and run the codefrom Head First Design Patterns, you should already have asrc/headfirst/factory directory. Just create a new directory, simulator/, in the factory/directory.) Create a directory classes/ to store your class files.

    Don't forget to create a file, simulator.properties, containing your properties (this file should be at the top level):

     buffalo = headfirst.factory.simulator.Buffalo horse = headfirst.factory.simulator.Horse cowboy = headfirst.factory.simulator.Cowboy cowgirl = headfirst.factory.simulator.Cowgirl
    

    Then, compile and run as follows:

     javac -d ./classes ./src/headfirst/factory/simulator/StampedeSimulatorTestDrive.java java -cp ./classes headfirst.factory.simulator.StampedeSimulatorTestDrive
    

    You should see the following output:

     Stampede Test Drive I'm a Buffalo I'm a Horse I'm a Cowboy I'm a Cowgirl
    
      
    http://today.java.net/im/a.gif