User Professor Hans Noodles
Professor Hans Noodles
Level 41

Factory design pattern

Published in the Java Developer group
6640 members
Hi, friend! Today we will continue to study design patterns. In this lesson, we're going to talk about factories. We will discuss the problem that this pattern solves and look at an example of how a factory can help you open a coffee shop. Additionally, I will give you 5 simple steps to create a factory. Factory design pattern - 1 To make sure that we're all on the same wavelength and that you'll quickly grasp this concept, you should be familiar with the following topics:
  • Inheritance in Java
  • Narrowing and widening of reference types in Java
  • Interaction between different classes and objects.

What is a factory?

The factory design pattern lets you control the creation of objects. The process of creating a new object is not super simple, but neither is it overly complicated. We all know that we need the new operator to create a new object. Perhaps it seems that there is nothing to control here, but that is not true. Suppose our application has a certain class that has many descendants. Difficulties can arise when it is necessary to create an instance of a specific class depending on certain conditions. A factory is a design pattern that helps solve the problem of creating various objects depending on certain conditions. How's that for an abstract concept? This will get clearer and more specific when we look at the example below.

Let's prepare various types of coffee

Suppose we want to automate a coffee shop. We need to teach our program how to make different types of coffee. To do this, we will create a coffee class and a few derivative classes to represent the types of coffee that we will prepare: Americano, cappuccino, espresso, and latte. Let's start with a general coffee class:

public class Coffee {
    public void grindCoffee(){
        // Grind the coffee
    }
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
Next, we'll create its child classes:

public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Our customers can order any type of coffee. Their orders need to be passed to the program. This can be done in many ways, for example, using String. But an enum is best for this. We'll create an enum and define enum fields that correspond to the types of coffee that can be ordered:

public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
Great. Now let's write the code for our coffee shop:

public class CoffeeShop {
    
    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = null;
        
        switch (type) {
            case AMERICANO:
                coffee = new Americano();
                break;
            case ESPRESSO:
                coffee = new Espresso();
                break;
            case CAPPUCCINO:
                coffee = new Cappucсino();
                break;
            case CAFFE_LATTE:
                coffee = new CaffeLatte();
                break;
        }

        coffee.grindCoffee();
        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Here's your coffee! Thanks! Come again!");
        return coffee;
    }
}
The orderCoffee method can be divided into two parts:
  1. Creation of a specific instance of coffee in a switch statement. This is where a factory does what it does — create a specific type depending on conditions.
  2. Preparation — this is the grinding, brewing, and pouring into a cup.
Here's what's important to know if you need to make changes to the method in the future:
  1. The steps involved in the preparation itself (grinding, brewing, and pouring into a cup) will remain unchanged (at least we are counting on this).
  2. But the assortment of coffees may change. Maybe we'll start making mocha... Frappu... Mochacci... Whatever, a new kind of coffee.
We can already be fairly confident that in the future we will have to make changes to the method's switch statement. It's also possible that in our coffee shop the orderCoffeemethod will not be the only place where we will create different types of coffee. As a result, changes will have to be made in several places. You probably already understand what I'm getting at. We need to refactor. Move the block responsible for creating coffee into a separate class for two reasons:
  1. We can reuse the coffee-making logic in other places.
  2. If the assortment changes, we won't have to edit the code everywhere coffee is created. It will be enough to change our code in just one place.
In other words, the time has come to set up a factory.

Setting up our first factory

To do this, we'll create a new class that will only be responsible for creating the necessary instances of coffee classes:

public class SimpleCoffeeFactory {
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new Americano();
                break;
            case ESPRESSO:
                coffee = new Espresso();
                break;
            case CAPPUCCINO:
                coffee = new Cappucino();
                break;
            case CAFFE_LATTE:
                coffee = new CaffeLatte();
                break;
        }
        
        return coffee;
    }
}
Congratulations! We have just implemented the factory design pattern in its simplest form (almost). It could have been even simpler if we made the createCoffee method static. But then we would lose two capabilities:
  1. The ability to inherit SimpleCoffeeFactory and override the createCoffee method.
  2. The ability to add the required factory implementation to our classes.
By the way, speaking of implementation... We need to return to the coffee shop and add our coffee-making factory.

Adding a factory to the coffee shop

Let's rewrite the coffee shop class using a factory:

public class CoffeeShop {

    private final SimpleCoffeeFactory coffeeFactory;

    public CoffeeShop(SimpleCoffeeFactory coffeeFactory) {
        this.coffeeFactory = coffeeFactory;
    }

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = coffeeFactory.createCoffee(type);
        coffee.grindCoffee();
        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Here's your coffee! Thanks! Come again!");
        return coffee;
    }
}
Excellent. Now we'll provide a concise description of the general structure of the factory design pattern.

5 steps to opening your own factory

Step 1. Your program has a class with several descendants, as in the diagram below: Factory design pattern - 2Step 2. You create an enum with a field for each child class:

    enum CatType {
        LION,
        TIGER,
        FLUFFY
    }
Step 3. Build your factory. Call it CatFactory. Here's the code:

class CatFactory {}
Step 4. In your factory, create a createCat method that takes a CatType argument. Here's the code:

    class CatFactory {
        public Cat createCat(CatType type) {
            
        }
    }
Step 5. In the body of the method, write a switch statement that enumerates the enum fields and creates an instance of the class that corresponds to the passed enum value:

class CatFactory {
        public Cat createCat(CatType type) {
            Cat cat = null;
            
            switch (type) {
                case LION:
                    cat =  new Fluffy();
                    break;
                case TIGER:
                    cat = new Tiger();
                    break;
                case FLUFFY:
                    cat =  new Lion();
                    break;
            }
            
            return cat;
        }
    }
Now you can run a factory like a boss. :) Factory design pattern - 3

How to practice

Reading is good, writing code is even better. If your name has an even number of letters, try creating your own virtual pizzeria. If your name has an odd number of letters, try creating a virtual sushi bar. If you have no name, you lucked out. Today you can relax.
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION