What is Factory Design Pattern?
Factory design pattern is one of the creational design pattern, which benefits the process of instantiation of objects. While using Factory design pattern, you need to firstly create a common interface. Then you create a factory class that decides which concrete class to instantiate. This pattern is commonly used in Java. One of the examples is valueOf
method of Integer
class, which is known as a factory method. Instead of using new Integer()
, you can simply use Integer.valueOf()
. This method simplifies the client creation logic and also encapsulates the creation details from the client. Let’s take a closer look on the logic:
1
2
3
4
5
6
7
8
public final class Integer {
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
...
}
It checks the cache and return the object if it exists. Otherwise, it returns a new object. With this factory method, clients do not need to know this cache logic. All they can see is one Integer object is created, no matter how it is created. Furthermore, if the logic of creation needs to be updated in the future, we can update inside valueOf
, with no need to update client side code.
Main advantages are commonly recoginized as below:
- Encapsulate creation details: Instead of exposing creation details to clients, put the details in factory.
- Refer to common interface and let factory decide which class to instantiate: From the viewpoint of clients, concrete classes are not declared. Clients use a common interface or abstract class, which makes it easy to update creation logic in the future without changing client code.
- Readable: The factory method name can be read-friendly. Compared with
new
, factory methods enhances readability.
Implementation in Java
Let’s use an example to demonstrate how Factory design pattern is implemented in Java. Suppose we have 2 kinds of phone: Apple iphone and Samsung. Clients might create one of them. Firstly, we create a common abstract class for phones which has an abstract function call
, and then create sub-classes for IPhone and Samsung which implement call
function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public abstract class Phone {
abstract void call();
}
public enum PhoneType {
IPHONE,
SAMSUNG,
}
public class IPhone extends Phone {
@Override
void call() {
System.out.println("Call an iPhone.");
}
}
public class Samsung extends Phone {
@Override
void call() {
System.out.println("Call a Samsung");
}
}
Now keep the idea of Factory design pattern in mind, we consider how clients create any types of Phones without directly calling the constructor of sub-classes. So we need a Factory class:
1
2
3
4
5
6
7
8
9
10
11
12
13
public class PhoneFactory {
public static Phone getPhone(PhoneType type) {
switch (type) {
case IPHONE -> {
return new IPhone();
}
case SAMSUNG -> {
return new Samsung();
}
default -> throw new IllegalArgumentException("Unknown Phone Type.");
}
}
}
Clients use static getPhone
function in the factory class by simply providing the type of Phone. Here clients do not need to use sub-classes. Instead, they use common interfaces or abstract classes. Concrete class is determined inside Factory.
1
2
3
4
5
6
7
8
9
// Create an IPhone instance
Phone iPhone = PhoneFactory.getPhone(PhoneType.IPHONE);
iPhone.call();
// Output: "Call an iPhone."
// Create a Samsung instance
Phone samsung = PhoneFactory.getPhone(PhoneType.SAMSUNG);
samsung.call();
// Output: "Call a Samsung."
At last, you can verify the results that first Phone instance’s call
function should refer to IPhone’s and second Phone instance’s call
function should refer to Samsung’s.
Comments powered by Disqus.