나를 기록하다
article thumbnail
반응형

JAVA 언어로 배우는 디자인 패턴 입문

Factory Method 패턴

Template Method 패턴은 상위 클래스에서 처리의 뼈대를 만들고, 하위 클래스에서 구체적인 처리의 살을 붙였다.

이 패턴을 인스턴스 생성 장면에 적용한 것이 Factory Method 패턴이다.

 

factory는 공장이다. 인스턴스를 생성하는 공장을 Template Method 패턴으로 구성한 것이 Factory Method 패턴이다.

 

Factory Method 패턴에서는 인스턴스 생성 방법을 상위 클래스에서 결정하되, 구체적인 클래스 이름까지는 결정하지 않는다. 구체적인 살은 모두 하위 클래스에서 붙인다. 이로써 인스턴스 생성을 위한 뼈대(프레임워크)와 실제 인스턴스를 생성하는 클래스를 나누어 생각할 수 있게 된다.


예제 프로그램

예제 프로그램 설명
신분증 카드(ID 카드)를 만드는 공장을 소재로 하고, 다섯 개의 클래스가 등장한다.
Product 클래스와 Factory 클래스는 인스턴스를 생성하는 뼈대(프레임워크) 역할을 하며, framework 패키지에 속해 있다. IDCard 클래스와 IDCardFactory 클래스는 뼈대에 살을 붙여 구체적인 내용을 구현하는 역할을 하며, idcard 패키지에 속해 있다. Main 클래스는 동작을 위한 클래스이다.

 

중점사항
framework 패키지 쪽을 보고 있는지, idcard 패키지 쪽을 보고 있는지 확인하라.
- 인스턴스를 생성하는 프레임워크 쪽(framework 패키지)
- 구체적인 내용을 구현하는 쪽(idcard 패키지)

 

패키지 이름 설명
framework Product 추상 메서드 use만 정의한 추상 클래스
framework Factory 메서드 create를 구현한 추상 클래스
idcard IDCard 메서드 use를 구현한 클래스
idcard IDCardFactory 메서드 createProduct, registerProduct를 구현한 클래스
이름 없음 Main 동작 테스트용 클래스

 

예제 프로그램의 클래스 다이어그램


Product 클래스

  • 제품을 표현한 클래스
  • 추상메서드 use만 선언되어 있음
  • 구체적인 use의 구현은 모두 Product의 하위 클래스에 맡긴다
  • '무엇이든 use(사용)할 수 있는 것'을 제품으로 규정
package ch04_FactoryMethod.framework;

public abstract class Product {
    public abstract void use();
}

Factory 클래스

  • Template Method 패턴
  • createProduct: 제품을 만든다
  • registerProduct: 만든 제품을 등록한다
  • 제품 만들기와 등록하기는 하위 클래스에서 구현한다
  • 이 프레임워크에서 공장이란 'create 메서드로 Product 인스턴스를 생성하는 것'으로 규정한다
  • Factory Method 패턴에서는 인스턴스를 생성할 때 Template Method 패턴을 사용한다
package ch04_FactoryMethod.framework;

public abstract class Factory {
    public final Product create(String owner) {
        Product p = createProduct(owner);
        registerProduct(p);
        return p;
    }

    protected abstract Product createProduct(String owner);

    protected abstract void registerProduct(Product product);
}

IDCard 클래스

  • 인식번호 카드를 나타내는 IDCard 클래스를 만든다
  • 프레임워크에서 분리되는 것임을 명시하고자 idcard 패키지라는 별도의 패키지를 만든다
  • IDCard 클래스를 제품 Product 클래스의 하위 클래스로서 정의한다
package ch04_FactoryMethod.idcard;

import ch04_FactoryMethod.framework.Product;

public class IDCard extends Product {
    private String owner;

    IDCard(final String owner) {
        System.out.println(owner + "의 카드를 만듭니다.");
        this.owner = owner;
    }

    @Override
    public void use() {
        System.out.println(this + "을 사용합니다.");
    }

    @Override
    public String toString() {
        return "[IDCard : " + owner + "]";
    }

    public String getOwner() {
        return owner;
    }
}

IDCardFactory 클래스

  • createProduct와 registerProduct 메서드 구현
  • createProduct: IDCard 인스턴스를 생성하여 실제로 제품을 만든다
  • registerProduct: 만들어진 제품을 등록한다
package ch04_FactoryMethod.idcard;

import ch04_FactoryMethod.framework.Factory;
import ch04_FactoryMethod.framework.Product;

public class IDCardFactory extends Factory {
    @Override
    protected Product createProduct(final String owner) {
        return new IDCard(owner);
    }

    @Override
    protected void registerProduct(final Product product) {
        System.out.println(product + "을 등록했습니다.");
    }
}

Main 클래스

package ch04_FactoryMethod;

import ch04_FactoryMethod.framework.Factory;
import ch04_FactoryMethod.framework.Product;
import ch04_FactoryMethod.idcard.IDCardFactory;

public class Main {
    public static void main(String[] args) {
        Factory factory = new IDCardFactory();
        Product card1 = factory.create("YUNA KIM");
        Product card2 = factory.create("JAEJUNG PARK");
        Product card3 = factory.create("ROY KIM");
        Product card4 = factory.create("KANGIN LEE");
        System.out.println("=======================================");
        card1.use();
        card2.use();
        card3.use();
        card4.use();
    }
}

실행 결과

YUNA KIM의 카드를 만듭니다.
[IDCard : YUNA KIM]을 등록했습니다.
JAEJUNG PARK의 카드를 만듭니다.
[IDCard : JAEJUNG PARK]을 등록했습니다.
ROY KIM의 카드를 만듭니다.
[IDCard : ROY KIM]을 등록했습니다.
KANGIN LEE의 카드를 만듭니다.
[IDCard : KANGIN LEE]을 등록했습니다.
=======================================
[IDCard : YUNA KIM]을 사용합니다.
[IDCard : JAEJUNG PARK]을 사용합니다.
[IDCard : ROY KIM]을 사용합니다.
[IDCard : KANGIN LEE]을 사용합니다.

Factory Method 패턴의 클래스 다이어그램

Factory Method 패턴의 클래스 다이어그램

Product(제품)

  • 이 패턴으로 생성되는 인스턴스가 가져야 할 인터페이스(API)를 결정하는 추상 클래스
  • 구체적인 내용은 하위 클래스 ConcreteProduct에서 결정한다.

 

Creator(작성자)

  • Product(제품)을 생성하는 추상 클래스
  • 구체적인 내용은 하위 클래스 ConcreteCreator가 결정한다.
  • Creator는 실제로 생성할 ConcreteProduct에 대해서 모른다.
  • Creator는 Product의 역할과 인스턴스 생성 메서드를 호출하면 Product가 생성된다는 것만을 알고 있다.

new를 사용해 실제 인스턴스를 생성하는 대신에, 인스턴스를 생성하는 메서드를 호출함으로써 구체적인 클래스 이름에 의한 속박에서 상위 클래스를 자유롭게 한다.

 

ConcreteProduct(구체적인 제품)

  • 구체적인 제품을 결정

 

ConcreteCreator(구체적인 작성자)

  • 구체적인 제품을 만들 클래스를 결정

Template Method에 관하여

프레임워크와 구체적인 내용

프레임워크와 구체적인 내용이라는 두 가지 측면에서 이야기했다. 이들은 각각 framework 패키지와 idcard 패키지로 나뉘어 있다.

 

예를 들어 같은 프레임워크를 사용하여 전혀 다른 '제품'과 '공장'을 만든다고 가정하자. TV 클래스 Television과 TV 공장 TelevisionFactory를 만들 수 있다. 이 경우 framework 패키지를 import하는 별개의 television 패키지를 만들게 된다.

 

여기서 framework 패키지 내용을 수정하지 않고도 전혀 다른 '제품'과 '공장'을 만들 수 있다는 점에 주목하라. framework 패키지 안에는 idcard 패키지를 import하지 않았다. Product 클래스나 Factory 클래스에 IDCard나 IDCardFactory라는 구체적인 클래스 이름이 적혀있지 않다. 따라서 새로운 클래스를 같은 프레임워크로 생성하는 경우에도 예를 들어 television 패키지를 import하는 것 같은 framework 패키지 수정은 전혀 필요 없다. 이것을 'framework 패키지는 idcard 패키지에 의존하지 않는다'고 표현한다.


인스턴스 생성 - 메서드 구현 방법

예제 프로그램에서 Factory 클래스의 createProduct 메서드는 추상 메서드이다. 즉, 이 메서드는 하위 클래스에서 구현해야 한다.

createProduct 메서드를 기술하는 방법은 두 가지가 있다.

추상 메서드(Abstract Method)

추상 메서드로 기술하면, 하위 클래스에서는 반드시 이 메서드를 구현해야만 한다. 구현되어 있지 않으면 컴파일할 때 검출된다.

abstract class Factory {
    public abstract Product createProduct(String name);
    ...
}

디폴트(Default) 구현

디폴트 구현을 준비해 두면, 하위 클래스에서 구현하지 않은 경우에 디폴트 구현이 사용된다.

class Factory {
    public Product createProduct(String name) {
        return new Product(name);
    }
    ...
}

단 이 경우 Product 클래스에 대해 직접 new를 실행하므로 Product 클래스를 추상 클래스로 둘 수는 없다.


패턴 이용과 개발자 간의 소통

디자인 패턴을 사용해 어떤 클래스군을 설계할 경우, 그 클래스군을 보수하는 사람에게 설계자가 의도한 디자인 패턴이 무엇인지 잘 전달할 필요가 있다.프로그램의 주석이나 개발 문서 안에 실제로 사용되는 디자인 패턴의 명칭과 의도를 기술해두는 것도 좋은 방법이다.


static Factory Method

인스턴스 생성을 위한 클래스 메서드(static 메서드) 전반을 Factory Method라고 부르는 경우가 있다. 이것은 GoF의 Factory Method 패턴과 다르지만, Java에서 인스턴스를 생성할 때 매우 자주 사용되는 기법이다. Java API 레퍼런스에서도 인스턴스 생성을 위한 클래스 메서드를 static Factory Method로 표현한다.

 

static Factory Method로서는 create, newInstance, getInstance 등의 이름이 자주 사용된다.

예시

java.security.Secure.Random의 getInstance 메서드

  • 난수 생성 알고리즘 이름을 지정해서 SecureRandom 인스턴스를 생성하는 static Factory Method
SecureRandom random = SecureRandom.getInstance("NativePRNG");

java.util.List의 of 메서드

  • 구체적인 요소를 주면 List 인스턴스를 생성하는 static Factory Method
  • "prao","rao","faker","chovy"로 구성된 List를 얻는다.
List<String> list = List.of("prao", "rao", "faker", "chovy");

java.util.Arrays의 asList 메서드

  • 지정된 배열이나 열겨한 요소로부터 List 인스턴스를 생성하는 static Factory Method
String[] arr = {"prao", "rao", "faker", "chovy"};
List<String> list1 = Arrays.asList(arr);
List<String> list2 = Arrays.asList("prao", "rao", "faker", "chovy");

java.lang.String의 valueOf 메서드

  • 다양한 형태의 문자열 표현을 얻는 static Factory Method
  • char형 'A'로부터 String형 "A"를 얻는다.
String string = String.valueOf('A');

java.time.Instant의 now 메서드

  • 현재 시간을 나타내는 Instant의 인스턴스를 생성하는 static Factory Method
Instant instant = Instant.now();

관련 패턴

  • Template Method 패턴
    • Factory Method 패턴은 Template Method 패턴의 전형적인 응용
    • 예제 프로그램의 create 메서드가 템플릿 메서드로 구현되어 있다
  • Singleton 패턴
    • Creator(또는 ConcreteCreator) 클래스는 대부분 Singleton 패턴으로 만들 수 있다.
    • 프로그램 안에서 인스턴스가 여러 개 존재할 필요가 별로 없다.
  • Conposite 패턴
    • Product(또는 ConcreteProduct)에서 Composite 패턴이 적용 가능하다.
  • Iterator 패턴
    • Iterator 패턴에서 iterator 메서드가 Iterator 인스턴스를 만들 때 Factory Method 패턴을 사용할 수 있다.
반응형
profile

나를 기록하다

@prao

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

profile on loading

Loading...