팩토리 메서드(Factory Method) 패턴 : 생성패턴
객체를 생성할 때 어떤 클래스의 인스턴스를 만들 지 서브 클래스에서 결정하게 한다.
상속을 통해 기능을 확장하게 하는 패턴이다. 슈퍼 클래스 코드에서는 서브클래스에서 구현할 메소드를 호출해서 필요한 타입의 오브젝트를 가져와 사용한다. 이 메소드는 주로 인터페이스 타입으로 오브젝트를 리턴하므로 서브 클래스에서 정확히 어떤 클래스의 오브젝트를 만들어 리턴할지는 슈퍼클래스에서는 알지 못한다.
이렇게 서브 클래스에서 오브젝트 생성 방법과 클래스를 결정할 수 있도록 미리 정의해둔 메소드를 팩토리 메소드라고 하고, 이 방식을 통해 오브젝트 생성 방법을 나머지 로직, 즉 슈퍼클래스의 기본 코드에서 독립시키는 방법을 팩토리 메소드 패턴이라고 한다.
Creator: 팩토리 메서드(서브 클래스가 구현할)를 정의한다.
ConcreteCreator: 팩토리 메서드를 구현하는 서브 클래스로, 객체를 생성한다.
Product: 팩토리 메서드가 생성할 객체의 공통 인터페이스
심플 팩토리 Simple Factory
패턴이 아닌 OOP에서 자주 쓰이는 관용구라고 보면 된다. 객체를 생성하는 클래스를 따로 두는 것.
이 심플 팩토리의 개념은 팩토리 메서드 패턴과 추상 팩토리 패턴의 base 역할을 한다.
템플릿 메소드 패턴과 팩토리 메서드 패턴
이름도 비슷하고 구성도 비슷하여 관계가 있어보이지만, 팩토리 메서드 자체는 생성패턴이고 템플릿 메소드 패턴은 행위패턴 이므로 목적부터 다르다. 즉 이 둘은 완전히 다른 패턴이다.
템플릿 메소드 패턴은 서브 클래스에서 오버라이딩 하여 구체적으로 알고리즘을 구현한다. 이때 알고리즘이 아닌 객체 생성을 맡게 된다면 이것이 팩토리 메서드 패턴이 되는 것이다.
즉, 이 둘은 구성이 같지만 말했다시피 목적이 생성이냐 행위인가에 따라 완전히 다른 패턴이 된다.
팩토리 메서드 패턴의 장단점
장점
- 수정에 닫혀있고 확장에는 열려있는 OCP 원칙을 지킬 수 있다.
- 객체 생성을 한 곳에서 처리하기 때문에 유지보수에 용이하다
단점
- 간단한 기능을 사용할 때보다 많은 클래스를 정의해야 하기 때문에 코드량이 증가한다.
자바에서의 팩토리
자바에서 팩토리를 사용하는 예로 Calendar에서 찾아볼 수 있다.
getInstance()를 호출할 때, 어떤 값을 인자로 넘겨주는 지에 따라서 다른 인스턴스를 반환한다.
System.out.println(Calendar.getInstance(Locale.forLanguageTag("th-TH-x-lvariant-TH")).getClass());
System.out.println(Calendar.getInstance(Locale.forLanguageTag("ja-JP-x-lvariant-JP")).getClass());
System.out.println(Calendar.getInstance().getClass());
//getInstance()
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
// If no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
조건문에 따라 객체 생성이 다른 것을 확인 할 수 있다.
Spring에서의 팩토리
스프링에서도 팩토리 메서드 패턴을 사용한다.
BeanFactory가 Product를 생성하는 Creator 역할을 하며, 이에 구현된 클래스들이 ConcreteCreator 역할을 한다.
따라서 각각의 getBean 메서드를 팩토리 메서드로 보면된다. BeanFactory 인터페이스에 상속된 다양한 팩토리 메서드를 통해 빈을 생성하는 것이다.
Animal Interface
public interface Animal {
void sound();
}
AnimalFactory
public abstract class AnimalFactory {
public Animal newInstance() {
Animal animal = createAnimal();
animal.sound();
return animal;
}
protected abstract Animal createAnimal();
}
Dog
public class Dog implements Animal{
@Override
public void sound() {
System.out.println("멍!멍!");
}
}
DogAnimalFactory
public class DogAnimalFactory extends AnimalFactory{
@Override
protected Animal createAnimal() {
return new Dog();
}
}
이렇게 구상을 해놓고 고양이를 추가한다고 해보자. 기존 소스를 건드릴 필요 없이 코드 추가만 해주면 된다.
Cat
public class Cat implements Animal {
@Override
public void sound() {
System.out.println("야옹~");
}
}
CatAnimalFactory
public class CatAnimalFactory extends AnimalFactory{
@Override
protected Animal createAnimal() {
return new Cat();
}
}
Client
AnimalFactory af2 = new CatAnimalFactory();
Animal cat = af2.newInstance();
기존 소스를 건드리지 않고, 고양이를 추가할 수 있었다.
Reference
'OOP > Design Pattern' 카테고리의 다른 글
05. 프로토타입(Prototype) 패턴 (0) | 2023.02.13 |
---|---|
04. 빌더(Builder) 패턴 (0) | 2023.02.13 |
03. 추상 팩토리(Abstract Method) 패턴 (0) | 2023.02.13 |
00. GoF 디자인 패턴 (0) | 2023.02.13 |
01. 템플릿 메소드(Template Method) 패턴 (0) | 2023.01.26 |