Abstract Factory Pattern in Java: A Creational Design Pattern

Neelesh Janga
6 min readNov 27, 2023

--

Introduction to Design Patterns

Design patterns are reusable solutions to common problems that occur during software development. They provide a way to solve issues in a flexible and elegant manner, making code more maintainable and scalable. One such design pattern is the Abstract Factory Pattern, which falls under the category of creational design patterns.

Creational Design Patterns

Creational design patterns deal with object creation mechanisms, attempting to create objects in a manner suitable to the situation. These patterns abstract the instantiation process, making a system independent of how its objects are created, composed, and represented.

Abstract Factory Pattern

The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes. It encapsulates a group of individual factories with a common theme, allowing clients to use the interface to create objects without being concerned about the specific implementation details.

Components of the Abstract Factory Pattern

  1. Abstract Factory Interface: This declares the creation methods for the abstract product objects.
  2. Concrete Factories: These are classes that implement the abstract factory interface. They are responsible for creating a family of related products.
  3. Abstract Product Interface: This declares the interface for a type of product object.
  4. Concrete Products: These are the classes that implement the abstract product interface. They define the properties and behaviors of the product objects.

Understanding the Code

Let’s delve into a practical example of the Abstract Factory Pattern using Java. The provided code demonstrates a mobile phone manufacturing scenario where different components, such as the camera and display, are created based on the type of mobile device (iOS or Android).

UML Diagram representing all the Interfaces and Classes used in the demo

Code Overview

  • Interfaces: Two interfaces, ICamera and IDisplay, define the abstract product types for the camera and display components.
  • Exception Class: InvalidMobile is an exception class that can be thrown when an invalid mobile type is encountered.
  • Concrete Implementations: OnePlusCamera, OnePlusDisplay, IPhoneCamera, and IPhoneDisplay are concrete implementations of the camera and display components for OnePlus and iPhone devices.
  • Mobile Class: The Mobile class represents a mobile phone and is responsible for using the abstract factory to create camera and display components.
  • Factory Interfaces: IMobileComponentFactory declares the abstract factory interface, and IPhoneFactory and OnePlusFactory are concrete factory implementations.
  • Testing the Pattern: In the AbstractFactoryTest class, a mobile phone is created based on the specified operating system, and its functionalities are tested.

Code Explanation

— Interfaces for Products (ICamera and IDisplay):

// ICamera
public interface ICamera {
void captureImage();
void recordVideo(String resolution);
}
// IDisplay
public interface IDisplay {
void displaySettings(int fps);
}
  • These interfaces define the abstract product types for the camera and display components. Any concrete implementation of a camera or display must adhere to these interfaces.

— Exception Class (InvalidMobile):

// InvalidMobile
public class InvalidMobile extends Exception {
@Override
public String getMessage() {
return "Mobile doesn't exist";
}
}
  • The InvalidMobile class is an exception class that is thrown when an attempt is made to create a mobile with an invalid type.

— Concrete Implementations of Products (OnePlusCamera, OnePlusDisplay, IPhoneCamera, IPhoneDisplay):

// OnePlusCamera
public class OnePlusCamera implements ICamera {

@Override
public void captureImage(){
System.out.println("📷 Capturing Image 🖼️");
}

@Override
public void recordVideo(String resolution) {
switch (resolution){
case "720" -> System.out.println("Recording started with 720p resolution");
case "1080" -> System.out.println("Recording started with 1080p resolution");
case "2k" -> System.out.println("Recording started with 2k resolution");
case "4k" -> System.out.println("Recording started with 4k resolution");
case "8k" -> System.out.println("Recording started with 8k resolution");
default -> System.out.println("IPhone currently doesn't support " + resolution + " Resolution! Choose between 720p, 1080p, 2k, 4k & 8k");
}
}
}
// OnePlusDisplay
public class OnePlusDisplay implements IDisplay {

@Override
public void displaySettings(int fps) {
switch (fps){
case 30 -> System.out.println("Display changed to 30 FPS");
case 60 -> System.out.println("Display changed to 60 FPS");
case 120 -> System.out.println("Display changed to 120 FPS");
default -> System.out.println("OnePlus currently doesn't support " + fps + " FPS");
}
}
}
// IPhoneCamera
public class IPhoneCamera implements ICamera {

@Override
public void captureImage(){
System.out.println("📷 Capturing Image 🖼️");
}

@Override
public void recordVideo(String resolution) {
switch (resolution){
case "1080" -> System.out.println("Recording started with 1080p resolution");
case "2k" -> System.out.println("Recording started with 2k resolution");
case "4k" -> System.out.println("Recording started with 4k resolution");
default -> System.out.println("IPhone currently doesn't support " + resolution + " Resolution! Choose between 1080p, 2k & 4k");
}
}
}
// IPhoneDisplay
public class IPhoneDisplay implements IDisplay {

@Override
public void displaySettings(int fps) {
switch (fps){
case 30 -> System.out.println("Display changed to 30 FPS");
case 60 -> System.out.println("Display changed to 60 FPS");
default -> System.out.println("IPhone currently doesn't support " + fps + " FPS");
}
}
}
  • These classes implement the ICamera and IDisplay interfaces, providing concrete functionality for OnePlus and iPhone camera and display components.

— Mobile Class (Mobile):

// Mobile
public class Mobile {
private IDisplay display;
private ICamera camera;

private Mobile() {
}

public Mobile(IMobileComponentFactory mobileComponentFactory) {
super();
System.out.println();
display = mobileComponentFactory.createDisplay();
camera = mobileComponentFactory.createCamera();
}

public void photo() {
camera.captureImage();
}

public void video() {
camera.recordVideo("1080");
}

public void video(String resolution) {
camera.recordVideo(resolution);
}

public void setDisplay(){
display.displaySettings(30);
}

public void setDisplay(int fps) {
display.displaySettings(fps);
}
}
  • The Mobile class represents a mobile phone and is responsible for using the abstract factory to create camera and display components.

— Factory Interfaces (IMobileComponentFactory):

// IMobileComponentFactory
public interface IMobileComponentFactory {
ICamera createCamera();
IDisplay createDisplay();
}
  • The IMobileComponentFactory interface declares the abstract factory methods for creating camera and display components.

— Concrete Factory Implementations (IPhoneFactory, OnePlusFactory):

// IPhoneFactory
public class IPhoneFactory implements IMobileComponentFactory {

public IPhoneFactory(){
System.out.println("🎉 Manufacturing IPhone 🎉");
}

@Override
public ICamera createCamera() {
return new IPhoneCamera();
}

@Override
public IDisplay createDisplay() {
return new IPhoneDisplay();
}
}
// OnePlusFactory
public class OnePlusFactory implements IMobileComponentFactory{

public OnePlusFactory(){
System.out.println("🎉 Manufacturing OnePlus 🎉");
}

@Override
public ICamera createCamera() {
return new OnePlusCamera();
}

@Override
public IDisplay createDisplay() {
return new OnePlusDisplay();
}
}
  • These classes implement the IMobileComponentFactory interface, providing concrete implementations for creating camera and display components for iPhone and OnePlus devices.

— Testing the Pattern (AbstractFactoryTest):

public class AbstractFactoryTest {
public static void main(String[] args) throws InvalidMobile {
String os = "android";

// Building a Mobile using Camera & Display Components
IMobileComponentFactory mobileComponentFactory = switch (os.toLowerCase()){
case "ios" -> new IPhoneFactory();
case "android" -> new OnePlusFactory();
default -> throw new InvalidMobile();
};

// Using manufactured Mobile and testing its functionalities like recording videos, capturing photos, setting display fps, etc
Mobile mobile = new Mobile(mobileComponentFactory);
mobile.photo();
mobile.video("8k");
mobile.setDisplay(120);
}
}
  • In this class, a mobile phone is created based on the specified operating system, and its functionalities are tested.

Execution Flow

Step-1. Selecting a Factory Based on Operating System:

IMobileComponentFactory mobileComponentFactory = switch (os.toLowerCase()){
case "ios" -> new IPhoneFactory();
case "android" -> new OnePlusFactory();
default -> throw new InvalidMobile();
};
  • The appropriate factory (IPhoneFactory or OnePlusFactory) is selected based on the specified operating system.
  • An instance of the Mobile class is created using the selected factory. The constructor of Mobile initializes the camera and display components using the factory.

Step-2. Creating a Mobile Phone:

Mobile mobile = new Mobile(mobileComponentFactory);
  • An instance of the Mobile class is created using the selected factory. The constructor of Mobile initializes the camera and display components using the factory.

Step-3. Using Mobile Functionalities:

mobile.photo();
mobile.video("8k");
mobile.setDisplay(120);
  • Various functionalities of the mobile phone, such as taking a photo, recording a video, and setting the display, are tested.

Conclusion

The Abstract Factory Pattern is a powerful design pattern that promotes flexibility and maintainability in software development. It allows for the creation of families of related objects without specifying their concrete classes, making it easier to introduce new variants of products.

In the provided example, the code structure demonstrates the separation of product interfaces, concrete product implementations, and factory interfaces. The Mobile class acts as a client that relies on abstract factories to create concrete product instances.

By understanding and implementing the Abstract Factory Pattern, developers can design systems that are adaptable to changing requirements, making it a valuable tool in the software design toolbox.

--

--

Neelesh Janga

Passionate about Java, Web Development, AI, Coding, DSA, Freelancing , Web development & Security. Always loves to collaborate and work together!!