Factory design pattern

In this article, I will write about the Factory Design Pattern in Java, why it is good and how to use it when writing a Java application.

Why should I care?

Perhaps you have heard about programming to interfaces or just not to program to implementations. Every time you use the new keyword you create a concrete implementation of the given interface/abstract class/class. And this is bad.

Why is this bad? You may ask. There is no problem with it theoretically, using new is the “only” way you can create a new object. At least until you find another way — but for now let’s stick with new. The only opponent of new is out a well-known friend in the IT: change.

If we live the best practice of coding to interfaces we can make our application work with future implementations of this interface. As a basic example let’s write a simple method:

public void getFirst(List<String> input) {
   if(input != null && !input.isEmpty())
       return input.get(0);
   return null;
}

Here we accept a List of String objects — the implementation does not matter. And List is an interface. We could naturally use ArrayList as expected parameter but this would limit some calls to our method. For example, someone has a LinkedList (which is a different implementation of List) and in this case, he/she has to convert all the elements into an ArrayList to call our method — so using List makes it more usable for others.

What is this design pattern for?

From the basic book of Design Patters (written by the so Called Gang of Four or GoF): “Defines an interface for creating an object, but lets subclasses decide which class to instantiate.”

I would extend it with the following: a factory enables deferring the instantiation of subclasses.

Starting with…

Let’s prepare our basic example where we can start.

We have a company which creates iOS applications. Our workflow looks like this:

package hu.japy.dev.patterns;

/**
* This class contains the AppStore where we "create" iOS apps.
*
* @author GHajba
*
*/
public class AppStore {
   public void orderApp() {

       final App app = new App();

       app.develop();
       app.test();
       app.debug();
       app.deliver();
   }
}

Currently there is only one type of application we are creating, however what about the other Apple devices (iPhone / iPad, Apple Watch and Apple TV) we could create applications for?

We can add an enum to our orderApp method to distinguish between the different target machines:

package hu.japy.dev.patterns;

/**
* Describes the available app types for our store.
*
* @author GHajba
*
*/
public enum AppType {
   IOS,
   WATCH,
   TV
}

In this case we need different implementations of the App class because every app has its own development / testing process. In this case we can either make the App class abstract or change it to an interface. I will use the abstract solution. As you know, the difference between the two approaches is that abstract classes can have default method-implementations which children can override (as with Java 8 interfaces can have one default method but they are limited to some point).

public void orderApp(AppType type) {

   App app;
   switch (type) {
   case IOS:
       app = new IOSApp();
       break;
   case TV:
       app = new TVApp();
       break;
   case WATCH:
       app = new WatchApp();
       break;
   default:
       app = new IOSApp();
       break;
   }

   app.develop();
   app.test();
   app.debug();
   app.deliver();
}

This is fine until the already mentioned foe (change) comes along. For example Apple develops new gears and we want to stay in market so we create apps for them too, and some other products of Apple vanish with time. In this case we have to change our orderApp method again:

public void orderApp(AppType type) {

   App app;
   switch (type) {
   case IOS:
       app = new IOSApp();
       break;
   case TV:
       app = new TVApp();
       break;
   case GLASS:
       app = new GlassApp();
       break;
   default:
       app = new GlassApp();
       break;
   }

   app.develop();
   app.test();
   app.debug();
   app.deliver();
}

As you can see, instantiating concrete implementations with new can really mess up our code — and you have to change this switch block is a new app type is provided and you have to know which class to instantiate.

Factory Pattern to the rescue!

However there is the Factory Pattern we can utilize and clean up this mess:

package hu.japy.dev.patterns;

/**
* A simple Factory which encapsulates (hides) which concrete implementation to use for a given AppType.
*
* @author GHajba
*
*/
public class AppFactory {
   public App createApp(AppType type) {
       App app;
       switch (type) {
       case IOS:
           app = new IOSApp();
           break;
       case TV:
           app = new TVApp();
           break;
       case GLASS:
           app = new GlassApp();
           break;
       default:
           app = new GlassApp();
           break;
       }
       return app;
   }
}

This is basically to move the bunch of code of decision making into a separate class ending with Factory. It seems like some lazy thing but it is the real separation of concerns. You do not want to do how to instantiate an app of a given type — you only care to process the workflow. And this factory class comes to help.

Now we can re-write the orderApp method like this:

public void orderApp(AppType type) {

   final App app = new AppFactory().createApp(type);

   app.develop();
   app.test();
   app.debug();
   app.deliver();
}

The given code is much clearer. In this case you only need to know that “There is an AppFactory which knows how to create apps of given types. Let’s get and use it!”.

From now on you do not need to know what implementation of the App interface you need to create for a given type. It is all decided by the AppFactory class. It can happen, that two types result in the same application — but you do not have to do this and you can concentrate on the order to process.

Static factories

Sometimes you can see factory classes which are static. This makes things easier because you do not need to instantiate a new version of the factory.

We could re-write our example factory above like this:

public static App createApp(AppType type) {
   App app;
   switch (type) {
   case IOS:
       app = new IOSApp();
       break;
   case TV:
      app = new TVApp();
       break;
   case GLASS:
       app = new GlassApp();
       break;
   default:
       app = new GlassApp();
       break;
   }
   return app;
}

Well, this is only a small change, adding a static modifier to the createApp method.

And then we can use it in the client like this:

public void orderApp(AppType type) {

   final App app = AppFactory.createApp(type);

   app.develop();
   app.test();
   app.debug();
   app.deliver();
}

Beside this Java instantiates this Factory class only once when your application starts so you can use some functionality all along the application — for example a unique number generation — but this is out of the scope of this article.

Conclusion

Using the Factory Design Pattern fulfills the Separation of Concerns principle, leverages your knowledge of how to get the right version for a given interface or abstract class and as a side-effect, it makes your code clearer.

However as with any of the Design Patterns do not start right away to use it in any case: make sure you can utilize all the functionality of the Design Pattern. If not it can make your code more complex and less readable!

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.