Building Cloud Expertise with centron - Our Tutorials

Whether you are a beginner or an experienced professional, our practical tutorials provide you with the knowledge you need to make the most of our cloud services.

Design Patterns in Java: The Abstract Factory Pattern

Learn all about the Abstract Factory Design Pattern in Java, a powerful method for structuring and creating classes. Discover the differences from conventional Factory Patterns and see practical examples of applying this design pattern. Enhance your Java programming skills and make your code architecture more efficient.

Abstract Factory

If you’re familiar with the Factory Design Pattern in Java, you’ll notice that we have a single Factory class. This Factory class returns different subclasses based on the provided input and uses if-else or switch statements to achieve this. In the Abstract Factory Pattern, we eliminate the if-else blocks and have a Factory class for each subclass. Then, there is an abstract Factory class that returns the subclass based on the input Factory class. At first, it may seem confusing, but once you see the implementation, it’s really easy to understand and grasp the slight difference between Factory and Abstract Factory Patterns. As in our Factory Pattern post, we use the same superclass and subclass.

Superclass and Subclass for the Abstract Factory Design Pattern

Computer.java


package com.journaldev.design.model;

public abstract class Computer {

    public abstract String getRAM();
    public abstract String getHDD();
    public abstract String getCPU();

    @Override
    public String toString(){
        return "RAM= "+this.getRAM()+", HDD="+this.getHDD()+", CPU="+this.getCPU();
    }
}

PC.java


package com.journaldev.design.model;

public class PC extends Computer {

    private String ram;
    private String hdd;
    private String cpu;

    public PC(String ram, String hdd, String cpu){
        this.ram=ram;
        this.hdd=hdd;
        this.cpu=cpu;
    }
    @Override
    public String getRAM() {
        return this.ram;
    }

    @Override
    public String getHDD() {
        return this.hdd;
    }

    @Override
    public String getCPU() {
        return this.cpu;
    }

}

Server.java


package com.journaldev.design.model;

public class Server extends Computer {

    private String ram;
    private String hdd;
    private String cpu;

    public Server(String ram, String hdd, String cpu){
        this.ram=ram;
        this.hdd=hdd;
        this.cpu=cpu;
    }
    @Override
    public String getRAM() {
        return this.ram;
    }

    @Override
    public String getHDD() {
        return this.hdd;
    }

    @Override
    public String getCPU() {
        return this.cpu;
    }

}

Factory Class for Each Subclass

First, we need to create an abstract Factory interface or class.

ComputerAbstractFactory.java


package com.journaldev.design.abstractfactory;

import com.journaldev.design.model.Computer;

public interface ComputerAbstractFactory {

    public Computer createComputer();

}

Note that the createComputer() method returns an instance of the Computer superclass. Now, our factory classes will implement this interface and return their respective subclass.

PCFactory.java


package com.journaldev.design.abstractfactory;

import com.journaldev.design.model.Computer;
import com.journaldev.design.model.PC;

public class PCFactory implements ComputerAbstractFactory {

    private String ram;
    private String hdd;
    private String cpu;

    public PCFactory(String ram, String hdd, String cpu){
        this.ram=ram;
        this.hdd=hdd;
        this.cpu=cpu;
    }
    @Override
    public Computer createComputer() {
        return new PC(ram,hdd,cpu);
    }

}

Similarly, we will have a factory class for the Server subclass.

ServerFactory.java


package com.journaldev.design.abstractfactory;

import com.journaldev.design.model.Computer;
import com.journaldev.design.model.Server;

public class ServerFactory implements ComputerAbstractFactory {

    private String ram;
    private String hdd;
    private String cpu;

    public ServerFactory(String ram, String hdd, String cpu){
        this.ram=ram;
        this.hdd=hdd;
        this.cpu=cpu;
    }

    @Override
    public Computer createComputer() {
        return new Server(ram,hdd,cpu);
    }

}

Consumer Class

Now, we will create a consumer class that serves as the entry point for client classes to create a subclass.

ComputerFactory.java


package com.journaldev.design.abstractfactory;

import com.journaldev.design.model.Computer;

public class ComputerFactory {

    public static Computer getComputer(ComputerAbstractFactory factory){
        return factory.createComputer();
    }
}

Note that it’s a simple class, and the getComputer method accepts an argument of type ComputerAbstractFactory and returns a Computer object. At this point, the implementation should become clear. Let’s write a simple test method and see how the Abstract Factory is used to obtain an instance of the subclass.

TestDesignPatterns.java


package com.journaldev.design.test;

import com.journaldev.design.abstractfactory.PCFactory;
import com.journaldev.design.abstractfactory.ServerFactory;
import com.journaldev.design.model.Computer;

public class TestDesignPatterns {

    public static void main(String[] args) {
        testAbstractFactory();
    }

    private static void testAbstractFactory() {
        Computer pc = com.journaldev.design.abstractfactory.ComputerFactory.getComputer(new PCFactory("2 GB","500 GB","2.4 GHz"));
        Computer server = com.journaldev.design.abstractfactory.ComputerFactory.getComputer(new ServerFactory("16 GB","1 TB","2.9 GHz"));
        System.out.println("AbstractFactory PC Config::"+pc);
        System.out.println("AbstractFactory Server Config::"+server);
    }
}

The output of the above program will be:


AbstractFactory PC Config::RAM= 2 GB, HDD=500 GB, CPU=2.4 GHz
AbstractFactory Server Config::RAM= 16 GB, HDD=1 TB, CPU=2.9 GHz

Advantages of the Abstract Factory Design Pattern

  • The Abstract Factory Design Pattern provides an approach to coding for the interface rather than the implementation.
  • The Abstract Factory Pattern is a “factory of factories” and can easily be extended to accommodate more products.

Unlock Your Potential with Our Cloud Solutions

Experience the full power of our cloud platform with a free trial. Enhance your Java development skills and streamline your projects with our robust, scalable, and secure cloud infrastructure. Start your trial today and see how our solutions can transform your software development process. Don't miss out on the opportunity to take your coding to the next level. Try it now!

Try for free!