CodeGym /Java блог /Случаен /Шаблони за проектиране: Фабричен метод
John Squirrels
Ниво
San Francisco

Шаблони за проектиране: Фабричен метод

Публикувано в групата
здрасти Днес ще продължим да изучаваме моделите на проектиране и ще обсъдим модела на фабричния метод. Шаблони за проектиране: Фабричен метод - 1 Ще разберете Howво представлява и за Howви задачи е подходящ този модел. Ще разгледаме този дизайн модел на практика и ще проучим неговата структура. За да сте сигурни, че всичко е ясно, трябва да разберете следните теми:
  1. Наследяване в Java.
  2. Абстрактни методи и класове в Java

Какъв проблем решава фабричният метод?

Всички модели на фабрично проектиране имат два типа участници: създатели (самите фабрики) и продукти (обектите, създадени от фабриките). Представете си следната ситуация: имаме фабрика, която произвежда автомобor с марката CodeGym. Той знае How да създава модели на автомобor с различни видове каросерии:
  • седани
  • комбита
  • купета
Бизнесът ни просперира толкова много, че в един прекрасен ден придобихме друг производител на автомобor — OneAuto. Като разумни собственици на бизнес, ние не искаме да губим клиенти на OneAuto и затова сме изпequalsи пред задачата да преструктурираме производството, така че да можем да произвеждаме:
  • CodeGym седани
  • CodeGym комбита
  • CodeGym купета
  • OneAuto седани
  • OneAuto комбита
  • OneAuto купета
Както виждате, instead of една група продукти, вече имаме две, като те се различават по някои детайли. Шаблонът за проектиране на фабричен метод е за случаите, когато трябва да създадем различни групи продукти, всеки от които има някои специфични характеристики. Ще разгледаме водещия принцип на този модел на практика, като постепенно преминаваме от простото към сложното, използвайки примера на нашето кафене, което създадохме в един от предишните уроци .

Малко за фабричния модел

Нека ви напомня, че преди това изградихме малко виртуално кафене. С помощта на една проста фабрика се научихме How да създаваме различни видове кафе. Днес ще преработим този пример. Нека си припомним How изглеждаше нашето кафене с неговата проста фабрика. Имахме час по кафе:

public class Coffee {
    public void grindCoffee(){
        // Grind the coffee
    }
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
И няколко дъщерни класа, съответстващи на конкретни видове кафе, които нашата фабрика може да произвежда:

public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Създадохме enum, за да улесним пequalsето на поръчки:

public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
Самата фабрика за кафе изглеждаше така:

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 Cappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new CaffeLatte();
                break;
        }
        
        return coffee;
    }
}
И накрая самото кафене изглеждаше така:

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;
    }
}

Модернизиране на проста фабрика

Нашето кафене работи много добре. Толкова много, че обмисляме разширяване. Искаме да отворим някои нови локации. Ние сме смели и предприемчиви, така че няма да пускаме скучни кафенета. Искаме всеки магазин да има специален привкус. Съответно, като начало ще отворим две локации: една италианска и една американска. Тези промени ще засегнат не само интериорния дизайн, но и предлаганите напитки:
  • в италианското кафене ще използваме изключително италиански марки кафе, със специално смилане и печене.
  • американското местоположение ще има по-големи порции и ще сервираме маршмелоу с всяка поръчка.
Единственото нещо, което остава непроменено, е нашият бизнес модел, който се е доказал като отличен. По отношение на codeа това се случва. Имахме 4 класа, съответстващи на нашите продукти:

public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Но сега ще имаме 8:

public class ItalianStyleAmericano extends Coffee {}
public class ItalianStyleCappucino extends Coffee {}
public class ItalianStyleCaffeLatte extends Coffee {}
public class ItalianStyleEspresso extends Coffee {}

public class AmericanStyleAmericano extends Coffee {}
public class AmericanStyleCappucino extends Coffee {}
public class AmericanStyleCaffeLatte extends Coffee {}
public class AmericanStyleEspresso extends Coffee {}
Тъй като искаме да запазим настоящия бизнес модел, искаме методът orderCoffee(CoffeeType type)да претърпи възможно най-малко промени. Разгледайте го:

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;
}
Какви опции имаме? Е, ние вече знаем How да напишем фабрика, нали? Най-простото нещо, което веднага идва на ум, е да напишем две подобни фабрики и след това да предадем желаната реализация на конструктора на нашето кафене. По този начин класът на кафенето няма да се промени. Първо, трябва да създадем нов фабричен клас, да го накараме да наследи нашата проста фабрика и след това да заменим createCoffee(CoffeeType type)метода. Нека напишем фабрики за създаване на кафе в италиански стил и кафе в американски стил:

public class SimpleItalianCoffeeFactory extends SimpleCoffeeFactory {

    @Override
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}

public class SimpleAmericanCoffeeFactory extends SimpleCoffeeFactory{

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }

}
Сега можем да предадем желаната фабрична реализация на CoffeeShop. Нека да видим How ще изглежда codeът за поръчка на кафе от различни кафенета. Например капучино в италиански и американски стил:

public class Main {
    public static void main(String[] args) {
        /*
            Order an Italian-style cappuccino:
            1. Create a factory for making Italian coffee
            2. Create a new coffee shop, passing the Italian coffee factory to it through the constructor
            3. Order our coffee
         */
        SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
        CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
        italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
        
        
         /*
            Order an American-style cappuccino
            1. Create a factory for making American coffee
            2. Create a new coffee shop, passing the American coffee factory to it through the constructor
            3. Order our coffee
         */
        SimpleAmericanCoffeeFactory americanCoffeeFactory = new SimpleAmericanCoffeeFactory();
        CoffeeShop americanCoffeeShop = new CoffeeShop(americanCoffeeFactory);
        americanCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
    }
}
Създадохме две различни кафенета, като предадохме желаната фабрика на всяка. От една страна, постигнахме целта си, но от друга страна... Това няHow си не им харесва на предприемачите... Нека да разберем Howво не е наред. Първо, изобorето от фабрики. Какво? Сега за всяко ново местоположение трябва да създадем собствена фабрика и в допълнение към това да се уверим, че съответната фабрика е предадена на конструктора, когато създаваме кафене? Второ, това все още е проста фабрика. Само леко модернизиран. Но ние сме тук, за да научим нов модел. Трето, не е ли възможен различен подход? Би било чудесно, ако можем да поставим всички въпроси, свързани с приготвянето на кафе, вCoffeeShopклас чрез свързване на процесите на създаване на кафе и обслужване на поръчки, като същевременно поддържа достатъчна гъвкавост за приготвяне на различни стилове кафе. Отговорът е да, можем. Това се нарича шаблон за проектиране на фабричен метод.

От проста фабрика до фабричен метод

За да разрешите задачата възможно най-ефективно:
  1. Връщаме createCoffee(CoffeeType type)метода в CoffeeShopкласа.
  2. Ще направим този метод абстрактен.
  3. Самият клас CoffeeShopще стане абстрактен.
  4. Класът CoffeeShopще има детски часове.
Да, приятел. Италианското кафене не е нищо повече от потомък на CoffeeShopкласа, който прилага createCoffee(CoffeeType type)метода в съответствие с най-добрите традиции на италианските баристи. Сега, стъпка по стъпка. Стъпка 1. Направете Coffeeкласа абстрактен. Имаме цели две семейства от различни продукти. Все пак италианското и американското кафе имат общ прародител - класа Coffee. Би било правилно да го направим абстрактно:

public abstract class Coffee {
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
Стъпка 2. Направете CoffeeShopабстракт, с абстрактен createCoffee(CoffeeType type)метод

public abstract class CoffeeShop {

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = createCoffee(type);

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

        System.out.println("Here's your coffee! Thanks! Come again!");
        return coffee;
    }

    protected abstract Coffee createCoffee(CoffeeType type);
}
Стъпка 3. Създайте италианско кафене, което е наследник на абстрактното кафене. Ние внедряваме createCoffee(CoffeeType type)метода в него, като се съобразяваме със спецификата на италианските рецепти.

public class ItalianCoffeeShop extends CoffeeShop {

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}
Стъпка 4. Правим същото за кафенето в американски стил

public class AmericanCoffeeShop extends CoffeeShop {
    @Override
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }
}
Стъпка 5. Вижте How ще изглеждат американското и италианското лате:

public class Main {
    public static void main(String[] args) {
        CoffeeShop italianCoffeeShop = new ItalianCoffeeShop();
        italianCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);

        CoffeeShop americanCoffeeShop = new AmericanCoffeeShop();
        americanCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);
    }
}
Честито. Току-що внедрихме шаблона за проектиране на фабричен метод, използвайки нашето кафене като пример.

Принципът зад фабричните методи

Сега нека разгледаме по-подробно Howво имаме. Диаграмата по-долу показва получените класове. Зелените блокове са класове създатели, а сините блокове са класове продукти. Шаблони за проектиране: Фабричен метод - 2Какви изводи можем да направим?
  1. Всички продукти са имплементации на абстрактния Coffeeклас.
  2. Всички творци са реализации на абстрактния CoffeeShopклас.
  3. Виждаме две паралелни йерархии на класове:
    • Йерархия на продуктите. Виждаме италиански потомци и американски потомци
    • Йерархия на творците. Виждаме италиански потомци и американски потомци
  4. Суперкласът CoffeeShopняма информация кой конкретен продукт ( Coffee) ще бъде създаден.
  5. Суперкласът CoffeeShopделегира създаването на конкретен продукт на своите потомци.
  6. Всеки наследник на CoffeeShopкласа имплементира createCoffee()фабричен метод в съответствие със собствените си специфики. С други думи, реализациите на класовете производители подготвят специфични продукти въз основа на спецификата на класа производители.
Вече сте готови за дефинирането на модела на фабричния метод . Моделът на фабричния метод дефинира интерфейс за създаване на обект, но позволява на подкласовете да избират класа на създадения обект. По този начин фабричен метод делегира създаването на екземпляр на подкласове. Като цяло запомнянето на определението не е толкова важно, колкото разбирането How работи всичко.

Структура на фабричен метод

Шаблони за проектиране: Фабричен метод - 3Диаграмата по-горе показва общата структура на модела на фабричния метод. Какво друго е важно тук?
  1. Класът Creator прилага всички методи, които взаимодействат с продуктите, с изключение на фабричния метод.
  2. Абстрактният factoryMethod()метод трябва да бъде имплементиран от всички наследници на Creatorкласа.
  3. Класът ConcreteCreatorимплементира factoryMethod()метода, който директно създава продукта.
  4. Този клас отговаря за създаването на конкретни продукти. Това е единственият клас с информация за създаването на тези продукти.
  5. Всички продукти трябва да имат общ интерфейс, т.е. те трябва да бъдат потомци на общ продуктов клас. Това е необходимо, така че класовете, които използват продукти, да могат да работят с тях като абстракции, а не със специфични реализации.

Домашна работа

Днес свършихме доста работа и проучихме шаблона за проектиране на фабричния метод. Време е да затвърдим материала! Упражнение 1. Направете работата, за да отворите друго кафене. Може да е кафене в английски or испански стил. Или дори в стил космически кораб. Добавете хранителни оцветители към кафето, за да го накарате да свети, и кафето ви ще бъде просто извън този свят! Упражнение 2. В последния урок имахте упражнение, при което създадохте виртуален суши бар or виртуална пицария. Сега вашето упражнение е да не стоите на едно място. Днес научихте How да използвате модела на фабричния метод във ваша полза. Време е да използвате тези знания и да разширите собствения си бизнес ;)
Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION