Creating objects in factories
Sometimes we have a hierarchy of classes with one base class (or interface) and several subclasses and need to create a new subclass object depending on its type. Instead of writing the new
operator in client code where we will use the objects, it is convenient to encapsulate the code for creating objects in a separated place and call it from the client code. These places are known as factories which produce instances of classes related to the same hierarchy. They allow us to simplify client code and protect it from changes when new classes are added to the hierarchy.
So, a factory is a way of creating objects when one part of a program (a class or a method) creates objects , and another one process them.
There are several kinds of factories: static factory, simple factory, factory method and abstract factory. In this lesson, we will study only two first of them. They are often called idioms, not design patterns. But two last factories are real design patterns and relies on similar principles like these idioms.
The hierarchy of computers
As an example, we will consider the following hierarchy of computers
class Computer {
private long ram;
private long cpu;
// getters and setters
}
class PC extends Computer {
// additional members
}
class Laptop extends Computer {
// additional members
}
Note, the base class Computer
can be an abstract class or interface, but it is not important in this topic.
Static factory idiom
The static factory is the simplest factory that we can write. It has one static method which creates objects of a hierarchy. The method takes a required type as a string or enum argument and returns a corresponding subclass instance through the base class.
To create new computers, we invoke the static factory passing a required type.
class ComputerStaticFactory {
public static Computer newInstance(String type) {
if (type.equals("Computer")) {
return new Computer();
} else if (type.equals("PC")) {
return new PC();
} else if (type.equals("Laptop")) {
return new Laptop();
}
return null; // if not a suitable type
}
}
switch
.public class FactoryClient {
public static void main(String args[]) {
Computer pc = ComputerStaticFactory.newInstance("PC");
System.out.println(pc instanceof PC); // prints "true"
Computer laptop = ComputerStaticFactory.newInstance("Laptop");
System.out.println(laptop instanceof Laptop); // prints "true"
}
}
It is not difficult to declare a new class that extends Computer
and modifies the factory to create instances of the new class.
There are several possible implementation features:
- the method
newInstance
ofComputerStaticFactory
takes an enum type to restrict possible values; - the method
newInstance
throws an exception if not a suitable type is passed instead of returningnull
; - move the method
newInstance
directly to the base of the hierarchy -Computer
class; - a factory can have multiple methods which produce instances of different classes or an instance of a default class.
Simple factory idiom
The simple factory idioms differ from the static factory because the method for creating objects is non-static.
class ComputerFactory {
// it may contain some fields
public Computer newInstance(String type) {
if (type.equals("Computer")) {
return new Computer();
} else if (type.equals("PC")) {
return new PC();
} else if (type.equals("Laptop")) {
return new Laptop();
}
return null;
}
}
ComputerFactory factory = new ComputerFactory();
Computer pc = factory.newInstance("PC");
Conclusion
So, we consider two factory idioms in action: static factory and simple factory. They are not really design patterns but useful in practice and other factories rely on the same principles.
According to the principles, we encapsulate the code for creating objects of a hierarchy in a special place (factory). A factory has a method which takes a type of required object as its parameter and returns a suitable instance to the client code. It protects client code from changing the class hierarchy and makes it simpler to understand and maintain.
Another thing to remember is that a factory may have multiple clients which need to create objects. They do not need to duplicate the same code that calls the new
operator to make an instance.