What is the factory design pattern?
In object-oriented programming, the factory design pattern is a creational design pattern that encapsulates the object-creational logic. The factory pattern relies on factory methods to create objects, and the caller is unaware of the classes of resulting objects the factory pattern returns.
Stationery stationery = ProductFactory.getProduct(stationeryName);
The stationery
can be a pen, pencil, book, etc.
When to use the factory pattern?
An ideal situation is when you have an interface and several classes that implement the interface (a typical object-oriented implementation). Based on factory method implementation logic, the factory method caller will receive objects of these subclasses. Therefore, the factory pattern is most suitable for creating objects dynamically at run-time.
In practice, let's take stationery as an example. Stationery can consider a product category. Pens, books, paper clips, etc., are sub-categories that belong to stationery. Therefore, stationery is a candidate for our interface, and sub-categories can represent subclasses that implement our interface.
How to implement the factory pattern?
The implementation is a factory for subclasses. That's what it is. The factory pattern returns one of the subclasses based on callers' requirements.
Let's look at a code example. We have a parent interface, Stationery
, that abstracts the basic behavior of a generic piece of stationery.
interface Stationary{
public String getProductInfo(){}
}
The Book class implements the Product interface with its properties pages and price. It also overrides parents' getProductInfo
with its implementation.
class Book implements Stationery
{
private int pages;
private BigDecimal price;
public Book(int pages, BigDecimal price)
{
this.pages = pages ;
this.price = price;
}
public String getProductInfo()
{
return "This book costs: $"+price+" and it has :" +pages + "pages";
}
}
The Pen
class implements the Stationery
interface and has its own properties, color
, and price
. Like the Book
class, Pen
overrides the getProductInfo()
method with its implementation.
class Pen implements Stationery
{
private String color;
public BigDecimal price;
public Pen(String color, BigDecimal price)
{
this.price = price;
this.color = color;
}
public String getProductInfo()
{
return "This pen costs: $"+price+" and it writes in :" +color;
}
}
The factory class is self-explanatory. The best practice is using an enum class to hold class names instead of hardcoded ones. If you are not new to object-oriented programming, you will notice that the ProductFactory
class is the essence of the factory design pattern in this example.
class ProductFactory
{
public static Product getProduct(String product)
{
if("Book".equalsIgnoreCase(product))
{
return new Book(500, new BigDecimal("24.95"));
}
if("Pen".equalsIgnoreCase(product))
{
return new Pen("Blue", new BigDecimal("0.95"));
}
else
return null;
}
}
Here is the code to test the implemented concept.
public class FactoryPattern{
public static void main(String args[])
{
Stationary book = ProductFactory.getProduct("Book");
System.out.println(book.getProductInfo());
Stationary pen = ProductFactory.getProduct("Pen");
System.out.println(pen.getProductInfo());
}
}
Note that we request objects from the factory twice. In both requests, we receive objects of the type Stationery
. But the actual sub-class differs based on the argument we passed to the getProduct()
method.
Conclusion
The factory design pattern encapsulates object-creational logic from its users by providing factory methods that instantiate objects based on arguments the factory methods receive. The resulting objects are the parent class type, and the implementing class is determined by the argument passed to the factory method. These behaviors make the factory design pattern ideal for creating objects dynamically at run-time.