Skip to main content

Breaking the Last Dependency

April 14, 2005

{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, and Cowgirl) 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, a Coyote) or reconfigure the
concrete classes (say you want to use the FastHorse class instead of
the generic Horse 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 have two
if-then-else clauses! 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 ugly if-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().
The forName() 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 to
addActor() (which in turn gets passed to the ActorFactory).
We don't have to change any code in the ActorFactory 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 the Actor type we want
in our code and pass it to addActor(), 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 to createActor(), 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 directory src/headfirst/factory/simulator.
(If you've already downloaded and run the
code
from Head First Design Patterns, you should already have a
src/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

width="1" height="1" border="0" alt=" " />
Elisabeth Freeman is an author, teacher and Java software developer.
Robert Simmons, Jr. started programming when floppy disks were really floppy and 64KB of RAM was considered state-of-the-art.