Lee's Grow up

[디자인패턴/Design Pattern] Factory Method 패턴 / 팩토리 메소드 패턴 본문

PROGRAMMING/디자인패턴

[디자인패턴/Design Pattern] Factory Method 패턴 / 팩토리 메소드 패턴

효기로그 2019. 11. 22. 11:19
반응형

관련 내용은 [자바 언어로 배우는 디자인 패턴 입문] 이라는 책의 공부 내용을 개인적으로 정리한 내용입니다.
처음 배우는 부분이기 때문에 틀린 부분이 있다면 지적해주시면 감사하겠습니다.

1. Factory Method 패턴이란?


상위 클래스에 알려지지 않은 구체 클래스를 생성하는 패턴이며, 하위 클래스가 어떤 객체를 생성할지 결정하도록 하는 패턴입니다. 또한 부모 클래스 코드에 구체 클래스 이름을 감추기 위한 방법으로도 사용합니다.

Factory Method라는 패턴 이름 때문에 객체를 생성하는 메소드라고 오해할 수 있지만, Template Method의 생성 버전이라고 볼 수 있다.

상속을 사용하지만 상위 클래스를 전혀 확장하지 않기 때문에 extends의 관계를 잘못 이용한 것처럼 보여질 수 있다.

객체 생성에 대한 정의는 하지만, 객체의 생성을 자식클래스(서브클래스)에 위임하는 패턴

2. Factory Method 패턴의 등장인물


이번 포스팅에서 사용될 요소들의 역할입니다.

  • Product( 제품 ) 의 역할
    1. Factory Method 패턴에서 생성되는 인스턴스가 가져야 할 인터페이스를 결정하는 추상 클래스
  • Creator ( 작성자 ) 의 역할
    1. Product의 역할과 인스턴스의 생성 메소드를 가집니다.
    2. new 연산자를 사용해 실제 인스턴스를 생성하는 대신에, 인스턴스 생성을 위한 메소드를 호출해서 구체적인 클래스 이름에 의한 속박에서 상위 클래스를 자유롭게 만듭니다.
  • ConcreteProduct ( 구체적인 제품 ) 의 역할
    1. Product의 내용을 정의합니다.
  • ConcreteCreator ( 구체적인 작성자 ) 의 역할
    1. Creator의 내용을 정의합니다.
Factory Method 패턴의 클래스 다이어그램

3. 예제


이제 Factory Method의 예제로 내용을 구체화 해보겠습니다.

예제에서 사용될 클래스 다이어그램

Factory 클래스

Template Method패턴을 사용하고 있습니다.create()라는 메소드를 통해 '생성'한다라는 정의만 하고 자세한 구현은

protected abstract Calculator createCalculator(Calculator.TYPE type, int a, int b) 메소드에게 위임합니다.

public abstract class Factory {
    public final Calculator create(Calculator.TYPE type,int a, int b ) {
        Calculator c = createCalculator(type, a, b);
        return c;
    }
    protected abstract Calculator createCalculator(Calculator.TYPE type, int a, int b);
}

CalculatorFactory 클래스

Factory 클래스를 상속 받으며 createCalculator() 메소드를 재정의해 실제 인스턴스를 생성해서 '제품을 만드는 일'을 담당하고 있습니다.

public class CalulatorFactory extends Factory {
    @Override
    protected Calculator createCalculator(Calculator.TYPE type,int a, int b) {
        switch(type) {
        case SUM : 
            return new SumCalculator(a, b);
        case DIV : 
            return new DivCalculator(a, b);
        case MUL : 
            return new MulCalculator(a, b);
        }
        return null;
    }
}

Calculator 클래스

제품에 대한 정의로 구현은 하위 클래스에게 위임합니다.

public abstract class Calculator {
    public enum TYPE {
        DIV, SUM, MUL
    }
    public abstract void getValue();
}

SumCalculator 클래스

CalCulator 클래스를 상속받으며 getValue()메소드를 재정의합니다.

public class SumCalculator extends Calculator{
    private int a;
    private int b;

    public SumCalculator(int a, int b) {
        System.out.println("덧셈 계산기의 입력 값은 : " + a + " / " + b + " 입니다");
        this.a = a;
        this.b = b;
    }
    @Override
    public void getValue() {
        System.out.println("연산 결과 : " + ( a + b));
    }
}

DivCalculator 클래스, MulCalculator 클래스

전체적인 로직은 SumCalCulator클래스와 같으나 재정의 하는 getValue()메소드 부분이 다르게 정의되어 있습니다.

// DivCalculator 클래스
@Override
public void getValue() {
    System.out.println("연산 결과 : " + ( a - b));
}

// MulCalculator 클래스
@Override
public void getValue() {
    System.out.println("연산 결과 : " + ( a * b));
}

Main 클래스

실행로직 클래스입니다. 계산기를 만들어서 사용해 보겠습니다.

public class Main {
    public static void main(String[] args) {

        Factory factory = new CalculatorFactory();

        Calculator sumCalculator = factory.create(Calculator.TYPE.SUM, 5, 3);
        sumCalculator.getValue();

        Calculator divCalculator = factory.create(Calculator.TYPE.DIV, 5, 3);
        divCalculator.getValue();

        Calculator mulCalculator = factory.create(Calculator.TYPE.MUL, 5, 3);
        mulCalculator.getValue();

    }
}
실행 결과
덧셈 계산기의 입력 값은 : 5 / 3 입니다
연산 결과 : 8
뺄셈 계산기의 입력 값은 : 5 / 3 입니다
연산 결과 : 2
곱셈 계산기의 입력 값은 : 5 / 3 입니다
연산 결과 : 15
그렇다면 사용하는 이유는 무엇일까?
  1. framework의 수정없이 새로운 '공장'과 '제품'추가가 가능합니다. ( 결합도가 낮다 )
    • 전혀 다른 '공학용 계산기'의 제품과 '공학용 계산기 공장'을 해당 framework에 추가해도 해당 클래스들의 내용의 추가만 있을 뿐, 기존 코드에 수정이 필요가 없습니다. 즉 결합도를 낮춰 유지보수에 용이합니다.
단점

패턴을 구현하기 위해 새로운 서브 클래스를 계속 생성하므로, 더 복잡해질 수 있다.

추가 내용

별도로 위 본문에서는 인스턴스 생성을 abstract Method 즉 추상 메소드로 구현을 하위 클래스에게 강제화 시켰지만,
디폴트로 구현을 하고, 재정의 하지 않을 경우 디폴트가 실행되게 하는방법과, 디폴트 시 에러를 발생 시키는 방법 등이 추가로 존재합니다.

default 구현 방식
Class Factory {
    protected Calculator createCalculator(Calculator.TYPE type,int a, int b) {
        // 필요한 로직 작성 ex) return new SumCalculator();
    }
}
에러발생 구현 방식
Class Factory {
    protected Calculator createCalculator(Calculator.TYPE type,int a, int b) {
        throw new FactoryMethodRuntimeException();
    }
}

4. 관련 패턴


  1. Template Method 패턴 : 팩토리 메소드 패턴은 Template Method 패턴의 응용 버전입니다. 본문 create() 메소드 link
  2. Singleton 패턴 : 보통 Creator역할 또는 ConcreteCreator을 수행 하는 클래스는 대부분 복수로 존재할 필요가 없기 때문에 Singleton 패턴으로 만들 수 있습니다. 단, 본문 예제에서는 사용하지 않았습니다. link
  3. Composite 패턴 : Product 역할에 적용 가능합니다.
  4. Iterator 패턴 : 해당 패턴의 iterator() 가 인스턴스를 작성할 때 Factory Method패턴이 사용되는 경우가 존재합니다 link
반응형
Comments