Cover Image

Nowadays, design patterns are not that popular to talk about. This could be related to the rapid iteration of web services, and the popularity of Micro Services. Design patterns were once considered useless dogma, thus as an anti-pattern, during the hype of Function Programming.

Of course, design patterns might have been thrown away as the essence of Object-Oriented Programming, because Functional Programming was preferred to OOP and was a hype during the time.

But if we think back, these design patterns are actually useful experiences extracted from practice. They do improve readability and scalability of programs to some extent. On the other hand, as well known conventions, design patterns deepen understanding between author and reader. In this sense, they can be considered as a medium of communication. Of course, this has to be used at correct scenarios. In terms of correct scenarios, we are talking about suitable scenarios — not using because we want to use, but for practical purpose.

In this blog post, I will introduce one of the most used design patterns: Factory Pattern, and talk about my understanding on it.

Introduction

Factory Pattern could arguably be the most widely used design pattern in Java. This type of design pattern comes under creational pattern as this pattern provides one of the best ways to create object. In Factory pattern, we create an object through a factory without exposing the creation logic to the client and refer to newly created object using a common interface.

Let’s first look at the simplist factory pattern.

Simple Factory Pattern

In my daily coding life, Simple Factory Pattern is enough to use. It suits well when we want to use a factory class to encapsulate creation logic of some product class that we would like to create. This product class can be extended by many sub-classes, which we are usually not interested in. We would rather like the factory to do its work, choose suitable sub-classes and create corresponding instances, by passing type information into the factory method.

Let’s look at a concret example.

Say we have a product class called Car, stands for cars. Different car factory can manufactory different cars, which can be considered as sub-classes of Car. So we can have BmwCar, VolvoCar, TeslaCar, and so on, demonstrated below.

A Simple Factory Example Diagram

Suppose we have a magic car factory called CarFactory, which is able to create multiple brands of cars. In order words, it can create BmwCar, VolvoCar and TeslaCar. When some type of Car is requested, the only thing to do is to tell the CarFactory what type of Car to create, and leave creating the car to this factory. How the car is manufactured does not concern, only that the factory will deliver the car fufilling all the requirements concerns.

As a client of this factory, how do I tell CarFactory which type of cars to create? It can be passed to createCar() method of CarFactory via method arguments.

1
2
3
4
5
6
7
8
Car car1 = SimpleCarFactory.createCar(CarType.BMW);
car1.drive();

Car car2 = SimpleCarFactory.createCar(CarType.VOLVO);
car2.drive();

Car car3 = SimpleCarFactory.createCar(CarType.TESLA);
car3.drive();

Inside this createCar() method, the factory can choose the actual car type to create based on arguments passed in.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class SimpleCarFactory {

  public static Car createCar(CarType type) {
    if (type == null) {
      throw new IllegalArgumentException("type cannot be null");
    }
    if (type == CarType.BMW) {
      return new BmwCar();
    } else if (type == CarType.VOLVO) {
      return new VolvoCar();
    } else if (type == CarType.TESLA) {
      return new TeslaCar();
    } else {
      throw new IllegalArgumentException("Unknown car type: " + type);
    }
  }
}

See complete code example at this github repo: https://github.com/gdong42/factory-pattern

Pros and Cons

What’s the benefit of this pattern? In this pattern, the caller side does not need to know the exact type implementation and its creation details, it only provides the required type name information. So this pattern reduces the complexity of the client, and decouples the creation logic of various implementation from invocation logic to some extent.

What about the cons? Let’s think about it in real world. There are few such factories. Because this magical factory needs to know how to create every product, which is almost impossible. OOP reflects the real world, therefore it is not reasonable to have a super magic factory that can create all type of products as well in the software world. In other words, each time when we want to add a new car type, we need to update createCar(). This should better be avoided.

Another problem is that SimpleCarFactory class has to depend on all car types, due to the truth that it contains all creation invocation logic in one giant factory. This leads to the client code coupling all product types, which might never be called by the client, and thus should be optional instead of mandatory dependencies. What we expect is that when we need some kind of product, we use a factory to create it, only with required dependency that has the underlying implementation module.

Real Example

In Java, an good example is the String.valueOf() method series.

The String class in Java represents a string. Sometimes, you need to get a string from a boolean or an integer. But String doesn’t provide constructors like String(Integer i) or String(Boolean b). Instead, it provides multiple simple factory methods String.valueOf(…).

1
2
int i = 12;
String integerAsString = String.valueOf(i);

A Step Further

Let’s take a step further and see how to improve this pattern to solve above problems.

There are two ways that might be useful to avoid the drawbacks mentioned above.

  1. Class Registration - maintain a mapping of the actual type and the name prameter of the type. This can be done with two flavors:

    • with reflection
    • without reflection
  2. Factory Method Pattern - the famous pattern described in the book of GoF’s design patterns.

Next let’s look at them in a bit more details.

Class Registration

The registration can be done by invoking a registering method of the factory. The actual creation of the factory can be done using two ways: with or without reflection. Using this technique, we can maintain the name and type mappings in a HashMap in the factory. To allow registering new mappings, we need to add a method allowing new types to be registered. This way, adding new product types no longer requires any change in the factory class code.

Class Registration, with reflection

For actual object creating logic, the type associated with the requesting type identifier is looked up from the internal mappings, and then the actual create instance logic is called by invoking the constructor using reflection. This can be demonstrated as followed.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public static class CarFactoryWithReflection {

  private Map<CarType, Class<? extends Car>> registeredCarTypes = new ConcurrentHashMap<>();

  private static CarFactoryWithReflection INSTANCE;

  private CarFactoryWithReflection() {
  }

  public static CarFactoryWithReflection instance() {
    if (INSTANCE == null) {
      INSTANCE = new CarFactoryWithReflection();
    }
    return INSTANCE;
  }

  public void registerCar(CarType type, Class<? extends Car> carClass) {
    this.registeredCarTypes.put(type, carClass);
  }

  public Car createCar(CarType type) {
    if (type == null) {
      throw new IllegalArgumentException("type cannot be null");
    }
    Class<? extends Car> carClass = registeredCarTypes.get(type);
    if (carClass == null) {
      throw new RuntimeException("Car type not registered: " + type);
    }
    try {
      Constructor<? extends Car> carConstructor = carClass
          .getDeclaredConstructor();
      return carConstructor.newInstance();
    } catch (Exception e) {
      throw new RuntimeException(
          "Car of type " + type + " cannot be instantiated", e);
    }
  }
}

This approach requires one to register a car type to the factory before calling the createCar method. It can be done by calling the registerCar method explicitly from the client code. For example,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
    public static void main(String[] args) {

      CarFactoryWithReflection.instance()
          .registerCar(CarType.BMW, BmwCar.class);
      CarFactoryWithReflection.instance()
          .registerCar(CarType.VOLVO, VolvoCar.class);
      CarFactoryWithReflection.instance()
          .registerCar(CarType.TESLA, TeslaCar.class);

      Car car1 = CarFactoryWithReflection.instance().createCar(CarType.BMW);
      car1.drive();

      Car car2 = CarFactoryWithReflection.instance().createCar(CarType.VOLVO);
      car2.drive();

      Car car3 = CarFactoryWithReflection.instance().createCar(CarType.TESLA);
      car3.drive();
    }

This reflection implementation has its own drawbacks.

  • Performance.
  • Cannot handle well when product has complex constructor parameters.
  • The registering logic seems a burden that the client needs to bear with.

Class Registration, without reflection

If for some reason we cannot use reflection, how to do the registration? We can move the registration to the concrete product class itself. This way, the factory does not require knowledge of the concrete product class. Adding new product type does not require changes to the factory as well.

We first add a new method in the abstract product interface, which is Car in our case.

1
2
3
4
5
6
7
public interface Car {

  ...

  Car create();

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class BmwCar implements Car {

  static {
    CarFactoryWithoutReflection.instance()
        .registerCar(CarType.BMW, new BmwCar());
  }

  ...

  @Override
  public Car create() {
    return new BmwCar();
  }
}

Above we register the Car instance into the Factory registration map. We also implement the create() method to produce new instances of BmwCar when required.

The factory and its usage look like below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  public static class CarFactoryWithoutReflection {

    private Map<CarType, Car> registeredCars = new ConcurrentHashMap<>();

    private static CarFactoryWithoutReflection INSTANCE;

    private CarFactoryWithoutReflection() {
    }

    public static CarFactoryWithoutReflection instance() {
      if (INSTANCE == null) {
        INSTANCE = new CarFactoryWithoutReflection();
      }
      return INSTANCE;
    }

    public void registerCar(CarType type, Car car) {
      this.registeredCars.put(type, car);
    }

    public Car createCar(CarType type) {
      if (type == null) {
        throw new IllegalArgumentException("type cannot be null");
      }
      Car car = registeredCars.get(type);
      if (car == null) {
        throw new RuntimeException("Car type not registered: " + type);
      }
      return car.create();
    }

    // make sure car type is registered to the factory before creating cars
    static {
      try {
        Class.forName("me.donggan.patterns.factory.simple.model.BmwCar");
        Class.forName("me.donggan.patterns.factory.simple.model.VolvoCar");
        Class.forName("me.donggan.patterns.factory.simple.model.TeslaCar");
      } catch (ClassNotFoundException cnfe) {
        cnfe.printStackTrace();
      }
    }

    public static void main(String[] args) {

      Car car1 = CarFactoryWithoutReflection.instance().createCar(CarType.BMW);
      car1.drive();

      Car car2 = CarFactoryWithoutReflection.instance().createCar(CarType.VOLVO);
      car2.drive();

      Car car3 = CarFactoryWithoutReflection.instance().createCar(CarType.TESLA);
      car3.drive();
    }

  }

This non-reflection implementation has its own drawbacks as well.

  • The client code need to call Class.forName() explicitly in order to make sure the registration logic is invoked, which is not convenient.
  • The concrete product class requires to know the presence of the Factory, and needs to call its register logic.

In the real world, the registration part is usually presented by some kind of framework, thus above drawbacks can be mitigated.

Factory Method Pattern

If we take a second thought on above non-reflection class registration, it has a create() method in the Car interface. This is an abstrace method that is used to encapsulate the creation logic to the concrete product itself. It looks more like what a factory does.

Besides, the example registers a Car instance in the static code, which is solely used to be called with the create() method to create other Car instances. See? the design is a bit awkward.

If we make the factory specific to each concrete car types, maybe we can use CarFactory type and BmwCarFactory, TeslaCarFactory and VolvoCarFactory subtypes to create concrete car types. This way some awkwardness above can be avoided. In fact, this leads us to the Factory Method Pattern.

In next installment of this article, I will talk about Factory Method Pattern.