JavaRush /Java Blog /Random EN /Factory design pattern

Factory design pattern

Published in the Random EN group
Hi, friend! Today we will continue to study design patterns with you. In this lecture we will talk about the Factory. We’ll discuss with you what problem is solved using this template, and look at an example of how a factory helps open a coffee shop. And I will also give you 5 simple steps to create a factory. Factory Design Pattern - 1To be on the same page with everyone and easily grasp the essence, you should be familiar with the following topics:
  • Inheritance in Java
  • Narrowing and expanding reference types in Java
  • Interaction between different classes and objects

What is a Factory?

The Factory design pattern allows you to control the creation of objects. The process of creating a new object is not that simple, but it is not too complicated either. We all know that to create a new object we must use the new. And it may seem that there is nothing to manage here, but this is not so. Difficulties may arise when our application has a certain class that has many descendants, and it is necessary to create an instance of a certain class depending on some conditions. Factory is a design pattern that helps solve the problem of creating different objects depending on some conditions. Abstract, isn't it? More specificity and clarity will appear when we look at the example below.

We create different types of coffee

Let's say we want to automate a coffee shop. We need to learn how to prepare different types of coffee. To do this, in our application we will create a coffee class and its derivatives: Americano, cappuccino, espresso, latte - those types of coffee that we will prepare. Let's start with the general coffee class:
public class Coffee {
    public void grindCoffee(){
        // перемалываем кофе
    }
    public void makeCoffee(){
        // делаем кофе
    }
    public void pourIntoCup(){
        // наливаем в чашку
    }
}
Next, let's create its heirs:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Our customers will order some type of coffee, and this information needs to be passed to the program. This can be done in different ways, for example using String. But it is best suited for these purposes enum. Let's create enumand define in it the types of coffee for which we accept orders:
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("Вот ваш кофе! Спасибо, приходите еще!");
        return coffee;
    }
}
The method orderCoffeecan be divided into two components:
  1. Creating a specific coffee instance in a block switch-case. This is where what the Factory does is the creation of a specific type depending on the conditions.
  2. The preparation itself is grinding, cooking and pouring into a cup.
What is important to know if you need to make changes to the method in the future:
  1. The preparation algorithm itself (grinding, cooking and pouring into a cup) will remain unchanged (at least we hope so).
  2. But the range of coffee may change. Maybe we'll start making mocha.. Mocha.. Mokkachi... God bless him, a new type of coffee.
We can already assume that in the future, with a certain degree of probability, we will have to make changes to the method, to the block switch-case. It is also possible that in our coffee shop the method orderCoffeewill not be the only place in which we create different types of coffee. Therefore, 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 will be able to reuse the logic of creating coffee in other places.
  2. If the range changes, we will not have to edit the code everywhere where coffee creation will be used. It will be enough to change the code in only one place.
In other words, it's time to cut down the factory.

We are sawing our first factory

To do this, let's create a new class that will be responsible only 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've just implemented the Factory design pattern in its simplest form. Although everything could be even simpler if the method was made createCoffeestatic. But then we would lose two possibilities:
  1. Inherit from SimpleCoffeeFactoryand override the createCoffee.
  2. Implement the required factory implementation in our classes.
Speaking of implementation. We need to go back to the coffee shop and implement our coffee making factory.

Introduction of a factory into a coffee shop

Let's rewrite our 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("Вот ваш кофе! Спасибо, приходите еще!");
        return coffee;
    }
}
Great. Now let’s try to schematically and concisely describe the structure of the Factory design pattern.

5 steps to opening your own factory

Step 1. In your program you have a class with several descendants, as in the picture below: Factory Design Pattern - 2Step 2. You create a class enumin which you define an enum variable for each descendant class:
enum CatType {
    LION,
    TIGER,
    BARSIK
}
Step 3. You build your factory. You call it MyClassFactory, the code is below:
class CatFactory {}
Step 4. You create a method in your factory createMyClassthat takes the variable - enum MyClassType. Code below:
class CatFactory {
    public Cat createCat(CatType type) {

    }
}
Step 5. You write a block in the body of the method switch-casein which you iterate through all enum values ​​and create an instance of the class corresponding to enumthe value:
class CatFactory {
        public Cat createCat(CatType type) {
            Cat cat = null;

            switch (type) {
                case LION:
                    cat =  new Barsik();
                    break;
                case TIGER:
                    cat = new Tiger();
                    break;
                case BARSIK:
                    cat =  new Lion();
                    break;
            }

            return cat;
        }
    }
Like a boss.

How to train

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 are anonymous, you are lucky. Today you can rest.
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION