Abstract Factory Pattern in Java: A Creational Design Pattern
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
- Abstract Factory Interface: This declares the creation methods for the abstract product objects.
- Concrete Factories: These are classes that implement the abstract factory interface. They are responsible for creating a family of related products.
- Abstract Product Interface: This declares the interface for a type of product object.
- 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).
Code Overview
- Interfaces: Two interfaces,
ICamera
andIDisplay
, 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
, andIPhoneDisplay
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, andIPhoneFactory
andOnePlusFactory
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
andIDisplay
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
orOnePlusFactory
) is selected based on the specified operating system. - An instance of the
Mobile
class is created using the selected factory. The constructor ofMobile
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 ofMobile
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.
Resources
- GitHub — https://github.com/Neelesh-Janga/23-Java-Design-Patterns/tree/main/src/com/neelesh/design/patterns/creational/abstract_factory
- Refactoring Guru — https://refactoring.guru/design-patterns/abstract-factory/java/example#example-0--app-Application-java
Social Accounts
- LinkedIn — https://www.linkedin.com/in/neelesh-janga/
- GitHub — https://github.com/Neelesh-Janga