팩토리 메서드 패턴이란
이 방법은 인스턴스 생성을 위한 인터페이스를 정의하고,
어떤 클래스의 인스턴스를 생성할지에 대한 결정은 서브클래스가 내리도록 합니다.
이로써 객체 생성과 클래스의 구현이 밀접하게 결합되는 것을 방지합니다.
팩토리 메서드 패턴의 구성
1. Creator
객체의 생성 과정을 추상화한 클래스 또는 인터페이스.
팩토리 메서드를 선언, 구체적인 객체 생성은 서브클래스(ConcreteCreator)가 담당한다.
2. ConcreteCreator
팩토리 메서드를 오버라이드 하여서, 구체적인 제품을 구현한다.
3. Product
생성될 객체들의 공통 인터페이스.
ConcreteProduct 클래스들이 구현해야 하는 메서드를 정의.
4. ConcreteProduct
Product 인터페이스를 구현하는 실제 객체들.
팩토리 메서드 패턴 다이어그램
예제: 커피집에서 제공하는 커피생성 과정
// Product 인터페이스
public interface Coffee {
void brew();
void serve();
}
// Creator 인터페이스(Factory)
public interface CoffeeFactory {
// 핵심
Coffee createCoffee();
default Coffee orderCoffee() {
Coffee coffee = createCoffee();
coffee.brew();
coffee.serve();
return coffee;
}
}
// Client
public class CoffeeShop {
public static void main(String[] args) {
CoffeeFactory espressoFactory = new EspressoFactory();
Coffee espresso = espressoFactory.orderCoffee();
CoffeeFactory americanoFactory = new AmericanoFactory();
Coffee americano = americanoFactory.orderCoffee();
CoffeeFactory cappuccinoFactory = new CappuccinoFactory();
Coffee cappuccino = cappuccinoFactory.orderCoffee();
}
}
위의 인터페이스와 클라이언트 코드로
Concrete Product와 Concrete Creator클래스들을 커피예제를 바탕으로 구성해보겠습니다.
커피숍에서 판매할 제품으로 에스프레소, 아메리카노, 카푸치노 세가지 제품군으로 정했습니다.
// ConcreteProduct
public class Espresso implements Coffee{
@Override
public void brew() {
System.out.println("Brewing Espresso!");
}
@Override
public void serve() {
System.out.println("Serving Espresso in a small cup");
}
}
// ConcreteCreator
public class EspressoFactory implements CoffeeFactory{
@Override
public Coffee createCoffee() {
return new Espresso();
}
}
이렇게 하면 구성은 이해가 되시죠
나머지 아메리카노, 카푸치노도 동일합니다. 구현해보겠습니다.
// ConcreteProduct
public class Americano implements Coffee{
@Override
public void brew() {
System.out.println("Brewing Americano!");
}
@Override
public void serve() {
System.out.println("Serving Espresso in a regular cup");
}
}
// ConcreteCreator
public class AmericanoFactory implements CoffeeFactory{
@Override
public Coffee createCoffee() {
return new Americano();
}
}
// ConcreteProduct
public class Cappuccino implements Coffee{
@Override
public void brew() {
System.out.println("Brewing Cappuccino!!");
}
@Override
public void serve() {
System.out.println("Serveing Cappucino with foam on top");
}
}
// ConcreteCreator
public class CappuccinoFactory implements CoffeeFactory{
@Override
public Coffee createCoffee() {
return new Cappuccino();
}
}
실행결과
실무사용 예
spring의 beanFactory가 대표적이네요.
1. Product는 애플리케이션에서 사용되는 모든 빈(Bean) 객체들입니다.
2. ConcreteProduct는 개발자가 정의한 구체적인 빈 객체들입니다.
3. Creator는 BeanFactory 인터페이스 또는 이를 확장한 ApplicationContext 인터페이스 역할을 합니다.
이들 인터페이스는 빈 객체를 생성하고 구성하는 메서드를 정의합니다(getBean 메서드 등)
4. ConcreteCreator는 BeanFactory 인터페이스를 구현한 클래스들입니다.
(XmlBeanFactory, ClassPathXmlApplicationContext, AnnotationConfigApplicationContext 등)
결론
장점
1. 유연성과 확장성: 팩토리 메서드 패턴은 새로운 종류의 객체를 생성하기 위해 기존 코드를 변경할 필요 없이
새로운 클래스를 추가하기만 하면 됩니다.
2. 객체 생성 코드의 중복 제거: 객체 생성에 관한 코드를 한 곳(구체적인 생성자)에 모아두어 코드 중복을 줄일 수 있습니다.
3. 클라이언트 코드와 객체 생성 코드의 분리: 클라이언트 코드는 구체적인 클래스 타입을 알 필요 없이 인터페이스 또는
추상 클래스를 통해 객체를 사용할 수 있습니다.
4. OCP(Open/Closed Principle) 준수: 기존 코드를 변경하지 않고 시스템을 확장할 수 있으므로 OCP원칙을 준수한다.
단점
1. 클래스 수의 증가: 각각의 구체적인 제품을 생성하기 위해 새로운 ConcreteCreator 클래스를 만들어야 하므로,
애플리케이션의 클래스 수가 증가할 수 있습니다.
2. 개념적 복잡성: 팩토리 메서드 패턴을 적용하면 설계가 다소 복잡해질 수 있습니다.
특히, 간단한 객체 생성 시나리오에서 패턴을 과도하게 사용하면, 오히려 이해하고 유지 보수하기 어려워질 수 있습니다.
3. 인터페이스 또는 추상 클래스에 의존: 클라이언트 코드가 인터페이스 또는 추상 클래스에 의존하게 되므로,
때때로 구체적인 객체의 특성을 완전히 활용하기 어려울 수 있습니다.
참고자료
GOF 디자인패턴 책
'디자인패턴' 카테고리의 다른 글
[디자인패턴-구조] 어댑터 패턴: 음악플레이어 예제를 통한 GOF 디자인 패턴의 이해 (WITH 자바) (0) | 2024.02.16 |
---|---|
[디자인패턴-생성] 싱글톤 패턴: 싱글톤 인스턴스 생성의 3가지 방법 (WITH 자바) (0) | 2024.02.13 |
[디자인패턴-생성] 프로토타입 패턴: 문서 템플릿 예제를 통한 GOF 디자인 패턴의 이해 (WITH 자바) (0) | 2024.02.12 |
[디자인패턴-생성] 추상팩토리 패턴: 가구 예제를 통한 GOF 디자인 패턴의 이해 (WITH 자바) (1) | 2024.02.12 |
[디자인패턴-생성] 빌더 패턴: 커피 예제를 통한 GOF 디자인 패턴의 이해 (0) | 2024.01.31 |