JavaRush /Java блог /Random UA /Паттерн проектування Factory

Паттерн проектування Factory

Стаття з групи Random UA
Привіт друг! Сьогодні ми продовжимо вивчати з тобою патерни проектування. У цій лекції говоритимемо про Фабрика. Обговоримо з тобою, яку проблему вирішують за допомогою даного шаблону, подивимося на прикладі того, як фабрика допомагає відкривати кав'ярню. А ще я дам 5 простих кроків для створення фабрики. Паттерн проектування Factory - 1Щоб бути з усіма на одній хвилі і легко вловлювати суть, тобі мають бути знайомі такі теми:
  • Спадкування в Java
  • Звуження та розширення посилальних типів у Java
  • Взаємодія між різними класами та об'єктами

Що таке Фабрика?

Шаблон проектування Фабрика дозволяє керувати створенням об'єктів. Процес створення нового об'єкта не те щоб простий, а й не надто складний. Усі ми знаємо, що для створення нового об'єкта необхідно використовувати оператор new. І може здатися, що тут нема чим керувати, проте це не так. Складнощі можуть виникнути, коли в нашому додатку є певний клас, який має безліч спадкоємців, і необхідно створювати екземпляр певного класу залежно від деяких умов. Фабрика — це шаблон проектування, який допомагає вирішити проблему створення різних об'єктів залежно від умов. Абстрактно, чи не так? Більше конкретики та ясності з'явиться, коли ми розглянемо приклад нижче.

Створюємо різні види кави

Припустимо, ми хочемо автоматизувати кав'ярню. Нам необхідно навчитися готувати різні види кави. Для цього в нашому додатку ми створимо клас кави та її похідні: американо, капучино, еспресо, латте – ті види кави, які ми готуватимемо. Почнемо із загального кавового класу:
public class Coffee {
    public void grindCoffee(){
        // перемалываем кофе
    }
    public void makeCoffee(){
        // делаем кофе
    }
    public void pourIntoCup(){
        // наливаем в чашку
    }
}
Далі створимо його спадкоємців:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Наші клієнти замовлятимуть будь-який вид кави, і цю інформацію потрібно передавати програмі. Це можна зробити різними способами, наприклад, використовувати String. Але найкраще для цих цілей підійде enum. Створимо enumта визначимо в ньому типи кави, на які ми приймаємо замовлення:
public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
Відмінно, тепер напишемо код нашої кав'ярні:
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;
    }
}
Метод orderCoffeeможна розділити на дві складові:
  1. Створення конкретного екземпляра кави в блоці switch-case. Саме тут відбувається те, що робить Фабрика – створення конкретного типу, залежно від умов.
  2. Саме приготування - перемолка, приготування та розлиття в чашку.
Що важливо знати, якщо потрібно буде вносити до методу зміни в майбутньому:
  1. Сам алгоритм приготування (перемолка, приготування та розлиття у чашку) залишиться незмінним (принаймні ми на це розраховуємо).
  2. А ось асортимент кави може змінитися. Можливо, ми почнемо готувати мок. Мокка. Моккачі… Господь з ним, новий вид кави.
Ми вже зараз можемо припустити, що в майбутньому, з певною часткою ймовірності, нам доведеться вносити зміни до методу, блоку switch-case. Також можливо, в нашій кав'ярні метод orderCoffeeбуде не єдиним місцем, в якому ми створюватимемо різні види кави. Отже, вносити зміни доведеться у кількох місцях. Тобі вже зрозуміло, до чого я хилю. Нам потрібно рефакторити. Винести блок, який відповідає за створення кави, в окремий клас із двох причин:
  1. Ми зможемо перевикористовувати логіку створення кави в інших місцях.
  2. Якщо асортимент зміниться, нам не доведеться правити код скрізь, де використовуватиметься створення кави. Достатньо змінити код тільки в одному місці.
Іншими словами, настав час запиляти фабрику.

Пабомо нашу першу фабрику

Для цього створимо новий клас, який відповідатиме лише за створення потрібних екземплярів класів кави:
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;
    }
}
Вітаю тебе! Ми щойно реалізували шаблон проектування Фабрика у найпростішому вигляді. Хоча все могло бути ще простіше, якщо зробити метод createCoffeeстатичним. Але тоді ми втратабо дві можливості:
  1. успадковуватися від SimpleCoffeeFactoryі перевизначати метод createCoffee.
  2. Впроваджувати необхідну реалізацію фабрики у наші класи.
До речі, про впровадження. Нам потрібно повернутися до кав'ярні та впровадити нашу фабрику зі створення кави.

Використання фабрики в кав'ярню

Перепишемо клас нашої кав'ярні з використанням фабрики:
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;
    }
}
Чудово. Тепер схематично та лаконічно спробуємо описати структуру шаблону проектування Фабрика.

5 кроків до відкриття власної фабрики

Крок 1. У тебе в програмі клас із кількома нащадками, як на малюнку нижче: Паттерн проектування Factory - 2Крок 2. Ти створюєш enum, в якому визначаєш enum-змінну для кожного класу-спадкоємця:
enum CatType {
    LION,
    TIGER,
    BARSIK
}
Крок 3. Ти будуєш свою фабрику. Називаєш її MyClassFactory, код нижче:
class CatFactory {}
Крок 4. Ти створюєш у своїй фабриці метод createMyClass, який приймає змінну- enum MyClassType. Код нижче:
class CatFactory {
    public Cat createCat(CatType type) {

    }
}
Крок 5. Ти пишеш у тілі методу блок switch-case, в якому перебираєш всі значення enum і створюєш екземпляр класу, відповідний enumзначенню:
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;
        }
    }
Як бос.

Як тренуватися

Читати - добре, писати код -ще краще. Якщо у твоєму імені парна кількість букв, спробуй створити свою віртуальну піцерію. Якщо у твоєму імені непарна кількість літер, спробуй створити віртуальний суші-бар. Якщо ти безіменний, тобі пощастило. Сьогодні можеш відпочивати.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ