JavaRush /Java Blog /Random-KO /디자인 패턴: FactoryMethod

디자인 패턴: FactoryMethod

Random-KO 그룹에 게시되었습니다
안녕하세요! 오늘은 계속해서 디자인 패턴을 공부하고 팩토리 메소드(FactoryMethod)에 대해 이야기해보겠습니다. 디자인 패턴: FactoryMethod - 1템플릿이 무엇인지, 이 템플릿이 어떤 작업에 적합한지 알아볼 수 있습니다. 실제로 이 디자인 패턴을 살펴보고 그 구조를 살펴보겠습니다. 위의 내용을 모두 명확하게 이해하려면 다음 주제를 이해해야 합니다.
  1. Java의 상속.
  2. Java의 추상 메소드 및 클래스.

팩토리 메소드는 어떤 문제를 해결합니까?

모든 공장 ​​디자인 패턴에는 제작자(공장 자체)와 제품(공장에서 생성된 개체)이라는 두 그룹의 참가자가 있습니다. 상황을 상상해 보십시오. AutoRush 브랜드로 자동차를 생산하는 공장이 있습니다. 그녀는 다양한 유형의 차체를 갖춘 자동차 모델을 만들 수 있습니다.
  • 세단
  • 스테이션 왜건
  • 쿠페
일이 순조롭게 진행되어 어느 화창한 날 우리는 OneAuto에 대한 우려를 흡수했습니다. 현명한 관리자로서 우리는 OneAuto 고객을 잃고 싶지 않으며, 우리의 임무는 다음을 생산할 수 있는 방식으로 생산을 재구성하는 것입니다.
  • AutoRush 세단
  • AutoRush 스테이션 왜건
  • 쿠페 오토러시
  • OneAuto 세단
  • OneAuto 스테이션 왜건
  • 쿠페 원오토
보시다시피 하나의 파생 상품 그룹 대신 일부 세부 사항이 다른 두 개의 파생 상품 그룹이 나타났습니다. 팩토리 메소드 디자인 패턴은 각각 특정성을 지닌 다양한 제품 그룹을 생성하는 문제를 해결합니다. 우리는 이전 강의 중 하나에서 만든 커피숍의 예를 사용하여 실제로 이 템플릿의 원리를 단순한 것에서 복잡한 것으로 점진적으로 이동하면서 고려할 것입니다 .

공장 템플릿에 대해 조금

상기시켜 드리겠습니다. 우리는 여러분과 함께 작은 가상 커피숍을 만들었습니다. 그 안에서 우리는 간단한 공장의 도움으로 다양한 종류의 커피를 만드는 방법을 배웠습니다. 오늘 우리는 이 예를 개선할 것입니다. 간단한 공장을 갖춘 우리 커피숍의 모습을 떠올려보자. 우리는 커피 수업을 가졌습니다:
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 {}
주문 접수의 편의를 위해 다음과 같은 전송 방식을 도입했습니다.
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("Вот ваш кофе! Спасибо, приходите еще!");
        return coffee;
    }
}

단순공장의 현대화

우리 커피숍은 잘 되고 있어요. 너무 많아서 확장을 고려하고 있습니다. 우리는 몇 가지 새로운 포인트를 열고 싶습니다. 진취적인 사람으로서 단조로운 커피숍을 만들지 않겠습니다. 나는 각자 자신만의 트위스트를 갖고 싶습니다. 따라서 우선 이탈리아 스타일과 미국 스타일의 두 가지 점을 공개하겠습니다. 변경 사항은 인테리어뿐만 아니라 음료에도 영향을 미칩니다.
  • 이탈리아 커피숍에서는 특별한 분쇄와 로스팅을 통해 이탈리아 커피 브랜드만을 사용합니다.
  • 미국식 부분은 조금 더 커질 것이며 주문할 때마다 녹은 마시멜로, 즉 마시멜로를 제공합니다.
변하지 않을 유일한 것은 그 자체로 잘 입증된 우리의 비즈니스 모델입니다. 코드 언어로 말하면 이런 일이 발생합니다. 우리는 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("Вот ваш кофе! Спасибо, приходите еще!");
    return coffee;
}
우리에게는 어떤 선택권이 있습니까? 우리는 이미 팩토리를 작성하는 방법을 알고 있습니다. 그렇죠? 즉시 떠오르는 가장 간단한 것은 두 개의 유사한 팩토리를 작성한 다음 생성자에서 커피숍에 필요한 구현을 전달하는 것입니다. 그러면 커피숍의 품격은 변하지 않을 것이다. 먼저, 새 팩토리 클래스를 생성하고 간단한 팩토리에서 상속하고 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에 전달할 수 있습니다. 다양한 커피숍에서 커피를 주문하는 코드가 어떻게 생겼는지 살펴보겠습니다. 예를 들어, 이탈리아 및 미국 스타일의 카푸치노:
public class Main {
    public static void main(String[] args) {
        /*
            Закажем капучино в итальянском стиле:
            1. Создадим фабрику для приготовления итальянского кофе
            2. Создадим новую кофейню, передав ей в конструкторе фабрику итальянского кофе
            3. Закажем наш кофе
         */
        SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
        CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
        italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);


         /*
            Закажем капучино в американском стиле
            1. Создадим фабрику для приготовления американского кофе
            2. Создадим новую кофейню, передав ей в конструкторе фабрику американского кофе
            3. Закажем наш кофе
         */
        SimpleAmericanCoffeeFactory americanCoffeeFactory = new SimpleAmericanCoffeeFactory();
        CoffeeShop americanCoffeeShop = new CoffeeShop(americanCoffeeFactory);
        americanCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
    }
}
우리는 두 개의 서로 다른 커피숍을 만들고 각각을 필요한 공장으로 이전했습니다. 한편으로는 목표를 달성했지만 다른 한편으로는... 뭔가가 기업가의 억제할 수 없는 영혼을 긁고 있습니다... 무엇이 문제인지 알아봅시다. 첫째, 공장이 풍부하다. 새로운 포인트를 만들 때마다 나만의 공장을 만들고, 커피숍을 만들 때 필요한 공장을 생성자에게 이전할 수 있는지 확인하는 것도 가능한가요? 둘째, 여전히 단순한 공장입니다. 조금 현대화되었습니다. 우리는 여기서 여전히 새로운 패턴을 연구하고 있습니다. 셋째, 다르게 할 수는 없을까? 교실 내에서 커피를 만드는 것에 대한 모든 질문을 현지화하고 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(){
        // делаем кофе
    }
    public void pourIntoCup(){
        // наливаем в чашку
    }
}
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("Вот ваш кофе! Спасибо, приходите еще!");
        return coffee;
    }

    protected abstract Coffee createCoffee(CoffeeType type);
}
Step 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단계. 미국식과 이탈리아식 라떼를 주문하는 방법을 살펴보겠습니다.
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);
    }
}
축하해요. 우리는 방금 커피숍에 팩토리 메소드 디자인 패턴을 구현했습니다.

팩토리 메소드의 작동 방식

이제 우리가 얻은 것을 자세히 살펴 보겠습니다. 아래 다이어그램은 결과 클래스를 보여줍니다. 녹색 블록은 생성자 클래스이고 파란색 블록은 제품 클래스입니다. 디자인 패턴: FactoryMethod - 2어떤 결론을 내릴 수 있습니까?
  1. 모든 제품은 추상 클래스의 구현입니다 Coffee.
  2. 모든 생성자는 추상 클래스의 구현입니다 CoffeeShop.
  3. 우리는 두 가지 병렬 클래스 계층 구조를 관찰합니다.
    • 제품의 계층 구조. 이탈리아 후손과 미국 후손이 보입니다.
    • 제작자의 계층 구조. 이탈리아 후손과 미국 후손이 보입니다.
  4. 슈퍼클래스에는 어떤 특정 제품 구현( )이 생성될지 CoffeeShop에 대한 정보가 없습니다 .Coffee
  5. 슈퍼클래스는 CoffeeShop특정 제품의 생성을 해당 하위 클래스에 위임합니다.
  6. 각 하위 클래스는 해당 세부사항에 따라 CoffeeShop팩토리 메소드를 구현합니다 . createCoffee()즉, 크리에이터 클래스의 구현 내에서는 크리에이터 클래스의 특성을 바탕으로 특정 제품을 준비하기로 결정됩니다.
이제 팩토리 메소드 패턴을 정의할 준비가 되었습니다 . 팩토리 메서드 패턴은 객체를 생성하기 위한 인터페이스를 정의하지만 하위 클래스가 생성할 인스턴스의 클래스를 선택할 수 있도록 허용합니다. 따라서 Factory 메서드는 인스턴스화 작업을 하위 클래스에 위임합니다. 일반적으로 정의를 기억하는 것은 사물이 어떻게 작동하는지 이해하는 것만큼 중요하지 않습니다.

팩토리 메소드 구조

디자인 패턴: FactoryMethod - 3위 다이어그램은 팩토리 메소드 패턴의 일반적인 구조를 보여줍니다. 여기서 또 무엇이 중요합니까?
  1. Creator 클래스에는 팩토리 메서드를 제외하고 제품과 상호 작용하는 모든 메서드의 구현이 포함되어 있습니다.
  2. 추상 메소드는 factoryMethod()클래스의 모든 자손에 의해 구현되어야 합니다 Creator.
  3. 클래스는 제품을 직접 생산하는 ConcreteCreator메소드를 구현합니다 .factoryMethod()
  4. 이 클래스는 특정 제품을 만드는 일을 담당합니다. 이는 이러한 제품을 만드는 방법에 대한 정보를 제공하는 유일한 수업입니다.
  5. 모든 제품은 공통 인터페이스를 구현해야 합니다. 즉, 공통 제품 클래스의 자손이어야 합니다. 이는 제품을 사용하는 클래스가 구체적인 구현이 아닌 추상화 수준에서 작동할 수 있도록 하기 위해 필요합니다.

숙제

그래서 오늘 우리는 꽤 많은 작업을 하고 팩토리 메소드 디자인 패턴을 연구했습니다. 이제 다루었던 자료를 통합할 시간입니다! 작업 1. 다른 커피숍을 여는 작업을 진행합니다. 영어 스타일이나 스페인어로 만들 수 있습니다. 아니면 우주선 스타일로도요. 커피에 식용색소를 넣어서 빛나게 해보자, 그러면 일반적으로 커피는 그냥 공간이 될 것이다! 과제 2. 지난 강의 에서는 가상 스시바나 가상 피자 가게를 만드는 과제를 맡았습니다. 당신의 임무는 가만히 서 있지 않는 것입니다. 오늘은 팩토리 메서드 패턴을 사용하여 성공하는 방법을 배웠습니다. 이제 이 지식을 활용하고 자신의 비즈니스를 확장할 때입니다 ;)
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION