디자인패턴:팩토리 패턴(Factory Pattern)

디자인패턴:팩토리 패턴(Factory Pattern)

1. 정의


 팩토리 패턴(Factory Pattern)

  • 모든 팩토리 패턴에서는 객체 생성을 캡슐화 한다.
  • 팩토리 메서드 패턴과 추상 팩토리 패턴이 있다.
  • 팩토리 메서드 패턴 : 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정한다.
  • 추상 팩토리 패턴 : 인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성할 수 있다. 추상 팩토리 패턴에는 팩토리 메서드 패턴이 포함될 수 있다.
  • 디자인원칙 중 ‘추상화된 것에 의존하도록 만들어라. 구상클래스에 의존하지 않도록 만든다.’에 기인한 패턴.

팩토리 패턴의 핵심은 클래스의 인스턴스를 만드는 것을 서브클래스에서 결정하도록 한다는 것입니다. 즉, new 키워드를 사용하는 부분을 서브클래스에 위임함으로 써 객체 생성을 캡술화하고 구상클래스에 대한 의존성이 줄어든다는 이점을 얻을 수 있습니다.

특히 구상클래스에 대한 의존성이 줄어드는 것은 의존성 뒤집기 원칙(Dependency Inversion Principle : DI)에 기인하는데, DI는 자바진영에서 널리 쓰이고 있는 Spring 프레임워크의 핵심 개념 중 하나입니다. 싱글톤패턴과 더불어 가장 유명하고 널리 쓰이는 디자인패턴 중 하나라고 할 수 있겠네요.

<팩토리 메서드 패턴 개념도>[1]

<추상 팩토리 패턴 개념도>[2]

2. 예시:개요


팩토리패턴 예시에는 아이언맨의 다양한 슈트들을 이용해 예시를 들어보겠습니다.

<국제적 금수저의 돈자랑.jpg[3]>

각 수트들은 각각의 고유한 기능들을 가지고 있기 때문에 특정 상황에 맞춰서 토니 스타크가 착용하는 슈트도 달라지게 됩니다. 우주탐사 시에는 스페이스아머, 심해탐사 시에는 하이드로 아머, 그리고 시네마틱 유니버스에 나왔던 헐크버스터 등을 예로 들 수 있습니다.

위와 같이 특정 상황을 입력으로 보고 입력에 따라 다른 수트 클래스를 만들어야 하는 상황을 간단한 코드로 표현하면 아래와 같이 될 것입니다.

Suit suit = null;
		
switch(type){
	case("space"): 
		suit = new SpaceSuit();
	break;
	case("hydro"):
		suit = new HydroSuit();
	break;
	case("stealth"):
		suit = new StealthSuit();
	break;
	default:
		suit = new CombatSuit();
}

이제 각 상황에 맞게 수트를 생성할 수 있게 되었습니다. 하지만 위와 같이 코드를 작성하게 되면 변경이나 확장할 요소가 생길 시 매번 코드를 추가, 제거해주어야 한다는 문제가 발생합니다.

위 문제는 객체 인스턴스를 생성하는(new 키워드를 사용하는) 부분을 별도의 인터페이스로 분리하면 해결할 수 있습니다. 이렇게 생성자를 별도의 인터페이스로 분리하여 객체를 만들어내는 공장(factory)로 이용하는 것팩토리 메서드 패턴입니다.

3-1. 예시:팩토리 메서드 패턴


이제 위 코드에서 인스턴스를 생성하는 부분을 인터페이스로 분리해보겠습니다.

UML로 그려보면 이런 모습이 되겠네요.  각각의 슈트 클래스들은 Suit 추상클래스를 상속받아 getName 메서드를 구현하고 SuitFactroy 추상클래스를 상속받은 TypeSuitFactroy 에서는 type별로 슈트 인스턴스를 생성하도록 하는 createSuit 메서드를 구현하도록 하겠습니다. 먼저 각 슈트 클래스 예시 코드 입니다.

//슈트 추상 클래스
public abstract class Suit {
	public abstract String getName();
}

//스텔스 슈트 클래스
public class StealthSuit extends Suit {

	@Override
	public String getName() {
		return "StealthSuit";
	}
	
}

다음은 슈트 인스턴스를 생성하는 팩토리 클래스 코드 입니다.

//슈트 팩토리 추상클래스
public abstract class SuitFactory {
	public abstract Suit createSuit(String type);
}

//슈트 팩토리 구현클래스
public class TypeSuitFactory extends SuitFactory{

	@Override
	public Suit createSuit(String type) {
		Suit suit = null;
		
		switch(type){
			case("space"): 
				suit = new SpaceSuit();
			break;
			case("hydro"):
				suit = new HydroSuit();
			break;
			case("stealth"):
				suit = new StealthSuit();
			break;
			default:
				suit = new CombatSuit();
		}
		
		return suit;
	}
}
static public void main(String[] args){
	TypeSuitFactory typeSuitFactory = new TypeSuitFactory();
	
	Suit suit1 = typeSuitFactory.createSuit("stealth");
	Suit suit2 = typeSuitFactory.createSuit("space");
	Suit suit3 = typeSuitFactory.createSuit("");
	
	System.out.println(suit1.getName());
	System.out.println(suit2.getName());
	System.out.println(suit3.getName());
	
	/**
	 * 결과 
	 * StealthSuit
	 * SpaceSuit
	 * CombatSuit
	 */
}

인스턴스 생성을 서브클래스로 위임한 결과 입니다. 최종 메인 메서드에서는 new 키워드를 사용하여 인스턴스를 생성한 부분이 없는 것을 확인할 수 있습니다. 이를 통해 메인 프로그램에서는 어떤 객체가 생성되었는지 신경 쓸 필요없이 반환된 객체만 사용하면 되고 슈트클래스에서 변경이 발생해도 메인 프로그램이 변경되는 것은 최소화할 수 있습니다.

3-2. 예시:추상 팩토리 패턴


다음 예시는 추상 팩토리 패턴에 대한 예시입니다. 위의 정의에서 추상 팩토리 패턴은 인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성할 수 있다고 했습니다. 즉, 연관된 서브 클래스를 그룹화할 수 있고 이것은 이 그룹을 자유롭게 교체할 수 있는 패턴이라고 할 수 있습니다.

먼저 UML을 그려보고 실제 구현 코드를 작성해보겠습니다.

SuitAbstractFactory 인터페이스를 작성하고 이를 상속받아 각 슈트를 생성하는 팩토리 클래스를 구성합니다. 그리고 SuitFactory에서는 이 팩토리를 파라미터로 받아 최종적으로 생성된 슈트 객체를 반환하게 됩니다. 이제 실제 코드를 작성해보겠습니다.

// 추상 팩토리 인터페이스
public interface SuitAbstractFactory {
	public Suit createSuit();
}

// CombatSuit 팩토리 클래스
public class CombatFactory implements SuitAbstractFactory {

	@Override
	public Suit createSuit() {
		return new CombatSuit();
	}

}

//팩토리 클래스를 파라미터로 받는 구현 클래스
public class SuitFactory {
	static public Suit getSuit(SuitAbstractFactory suitAbstractFactory){
		return suitAbstractFactory.createSuit();
	}
}
Suit suit1 = SuitFactory.getSuit(new CombatFactory());
Suit suit2 = SuitFactory.getSuit(new SpaceFactory());
Suit suit3 = SuitFactory.getSuit(new StealthFactory());

System.out.println(suit1.getName());
System.out.println(suit2.getName());
System.out.println(suit3.getName());


/**
 * 출력결과 
 * CombatSuit
 * SpaceSuit
 * StealthSuit
*/

위 결과 코드와 같이 슈트별 팩토리 클래스를 파라미터로 넘겨 각 슈트객체를 반환 받아 사용할 수 있습니다. 이렇게 되면 팩토리 클래스 교체 만으로 조금 더 유연하게 기능의 수정, 확장에 대처할 수 있게 됩니다. 코드 상으로는 if-else 구문을 제거하여 조금 더 깔금하게 코드를 구성할 수 있습니다.

4. 마무리


지금까지 팩토리패턴에 대해 알아봤습니다.

자세한 예제 코드는 깃헙 스터디 레파지토리에 올려놨습니다.

이상하거나 수정, 추가해야할 부분이 있다면 댓글이나 메일로 연락주세요:)

 

The following two tabs change content below.
  1. [1]출처:위키백과:팩토리 메서드 패턴
  2. [2]출처:제타위키:추상 팩토리 패턴
  3. [3]출처:나무위키-아이언맨-슈트

One Reply to “디자인패턴:팩토리 패턴(Factory Pattern)”

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.