Abstract Factory Pattern

Definition

The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.

Figure 01. Abstract Factory class diagram. Ref: http://www.oodesign.com/abstract-factory-pattern.html

Scenario

A telecom company wants to have a system that could register the different Internet offers that they have for their customers. Between all their requirements, they mentioned that with the time new internet offers may come and that for each internet plan the user have to have also a router with capabilities according to the plan that they choose. The routers are provided by the internet company.

Reviewing the requirements mentioned, we noticed that the costumer that choose an internet plan is linked to a router to have the internet connection. Because there can be different internet plans, that can have different routers, we can use a factory that is going to link both products (products in the sense of a Factory are the Objects that are created from it) : The Internet Plan and the Internet Hardware.

AbstractFactoryPattern

Figure 02. Class diagram of the solution proposed for the case study *

*Click on the figure to magnify it

As it can be seen in the previous figure our Abstract Factory (InternetPlanAbstractFactory) is going to be the responsible to delegate the creation of specific products by specific factories (BasicInternetFactory and FastInternetFactory).

InternetPlanAbstractFactory

package chris.desingpatterns.factory.factories;

import chris.desingpatterns.factory.products.hardware.AbstractInternetHardware;
import chris.desingpatterns.factory.products.plans.AbstractInternetPlan;

public interface InternetPlanAbstractFactory {
    /**
     * Method that will create an Internet Plan.
     * 
     * @return
     */
    public AbstractInternetPlan createInternetPlan();
    
    /**
     * Method that will create an Internet Hardware
     * 
     * @return
     */
    public AbstractInternetHardware createInternetHardware();

}

In the previous snippet AbstractInternetPlan and AbstractInternetHardware are the abstract products that can be created by our factories. For example the FastInternetFactory that will create products related to a Fast Internet Plan would look like this:

FastInternetFactory

package chris.desingpatterns.factory.factories;

import chris.desingpatterns.factory.products.hardware.AbstractInternetHardware;
import chris.desingpatterns.factory.products.hardware.FastInternetHardware;
import chris.desingpatterns.factory.products.plans.FastInternetPlan;
import chris.desingpatterns.factory.products.plans.AbstractInternetPlan;

public class FastInternetFactory implements InternetPlanAbstractFactory {
    
    /**
     * {@inheritDoc}
     */
    public AbstractInternetPlan createInternetPlan() {
        return new FastInternetPlan();
    }
    
    /**
     * {@inheritDoc}
     */
    public AbstractInternetHardware createInternetHardware() {
        return new FastInternetHardware();
    }

}

Now this specific factory will create these products: FastInternetPlan and FastInternetHardware. If the requirements change it is easier to modify the factories to get the products that we need.

The client in this case that will use a Factory to get the right products is the Customer class:

Customer

package chris.desingpatterns.factory;

import chris.desingpatterns.factory.factories.InternetPlanAbstractFactory;
import chris.desingpatterns.factory.products.hardware.AbstractInternetHardware;
import chris.desingpatterns.factory.products.plans.AbstractInternetPlan;

public class Customer {
    private String name;
    private InternetPlanAbstractFactory internetFactory;
    private AbstractInternetHardware internetHardware;
    private AbstractInternetPlan internetPlan;
    
    public Customer(String name) {
        this.name = name;
    }
    
    /**
     * Method that will set the specific
     * Internet plan and Internet hardware
     * for a customer.
     * 
     */
    public void setUpInternetService() {
        internetHardware = internetFactory.createInternetHardware();
        internetPlan = internetFactory.createInternetPlan();
    }

    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public void setInternetFactory(InternetPlanAbstractFactory internetFactory) {
        this.internetFactory = internetFactory;
    }

    public AbstractInternetHardware getInternetHardware() {
        return internetHardware;
    }

    public AbstractInternetPlan getInternetPlan() {
        return internetPlan;
    }

}

As can be seen here the client (Customer) just relies in abstract classes or interfaces:

  • InternetPlanAbstractFactory
  • AbstractInternetHardware
  • AbstractInternetPlan

Here fits perfectly well the Design Principle:

Depend upon abstractions. Do not depend upon concrete classes.

Finally the main class that will run this case study:

Main

package chris.desingpatterns.factory;

import chris.desingpatterns.factory.factories.BasicInternetFactory;
import chris.desingpatterns.factory.factories.FastInternetFactory;
import chris.desingpatterns.factory.factories.InternetPlanAbstractFactory;
import chris.desingpatterns.factory.products.hardware.AbstractInternetHardware;
import chris.desingpatterns.factory.products.plans.AbstractInternetPlan;

public class Main {

    /**
     * Method that will run the implementation of the 
     * Abstract Factory Pattern.
     * 
     * For this example we will have two customers:
     * - CustomerA with Basic Internet Plan.
     * - CustomerB with Fast Internet. 
     * 
     * Thanks to the factory, the required characteristics
     * of the Internet plan such as Internet Speed and costs
     * are correctly determined in the factories, without 
     * setting this extra information in other methods.
     * 
     * Factories are easy to maintain in case a New Internet
     * plan would be required, without changing any other 
     * classes or methods.
     * 
     * @param args
     */
    public static void main(String[] args) {
        //Customer A: 
        Customer customerA = new Customer("Christian");
        // Customer choose the Internet plan: Basic Internet
        setInternetPlan(customerA, new BasicInternetFactory());
        // The Serial Number of the Router 
        String serialNumberA = "11233654564AXB";
        setHardwareSerial(customerA, serialNumberA);
        printInfo(customerA);
        
        //Customer B:
        Customer customerB = new Customer("Kasia");
        // Customer choose Fast Internet
        setInternetPlan(customerB, new FastInternetFactory());
        // The serial number of the router
        String serialNumberB = "PSN123456";
        setHardwareSerial(customerB, serialNumberB);
        printInfo(customerB);
    }
    
    
    private static void setInternetPlan(Customer customer, 
            InternetPlanAbstractFactory internetFactory){
        
        customer.setInternetFactory(internetFactory);
        customer.setUpInternetService();
        
    }
    
    private static void setHardwareSerial(Customer customer, String serialNumber){
        AbstractInternetHardware hardwareOwned = customer.getInternetHardware();
        hardwareOwned.setSerialNumber(serialNumber);
    }
    
    private static void printInfo(Customer customer) {
        System.out.println("--------------------------------------");
        System.out.println("Hello: " + customer.getName());
        AbstractInternetPlan internetPlan = customer.getInternetPlan();
        
        System.out.println("You have chosen the following plan: " + 
                internetPlan.getName());
        
        System.out.println(internetPlan.getDescription());
        AbstractInternetHardware hardware = customer.getInternetHardware();
        
        System.out.println("You will receive the following device from us: " + 
                hardware.getModel() + " S/N: " + hardware.getSerialNumber());
        
        System.out.println(hardware.getCapabilities());
        System.out.println("TOTAL COST: " + internetPlan.getCost() + " Euros");
        System.out.println("--------------------------------------");
    }

}

That is it folks Hot smile!
As always, you can download the source code and dig into it to see how it works and maybe modify it to see how flexible de application of this pattern is:

Abstract Factory Pattern

Do you have any recommendations or amendments to be done, please let me know to improve this post!

Leave a comment