일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 개발자 취업
- 디자인 패턴
- 파이썬
- KPT회고
- 취업리부트코스
- 1주일회고
- 인프콘 2024
- 프로그래머스
- jwt
- 프로그래머스 이중우선순위큐
- 구글 OAuth login
- Spring multimodule
- @FeignClient
- jwttoken
- spring batch 5.0
- JavaScript
- 개발자부트캠프추천
- 전략패턴 #StrategyPattern #디자인패턴
- 빈 충돌
- DesignPattern
- 코딩테스트 준비
- 빈 조회 2개 이상
- 항해99
- TiL
- 커스텀 헤더
- 디자인패턴
- infcon 2024
- 단기개발자코스
- 99클럽
- Python
- Today
- Total
m1ndy5's coding blog
팩토리 메소드 패턴(Factory Method Pattern) 본문
팩토리 메소드 패턴 정의
부모 클래스에 알려지지 않은 구체 클래스를 생성하는 패턴이며 자식 클래스가 어떤 객체를 생성할지를 결정하도록 하는 패턴(출처 : 위키백과)
즉 슈퍼클래스에서 new를 사용해서 직접적인 인스턴스를 생성하지 않고 자식클래스에서 생성하도록 한다는 뜻이다.
객체 생성 팩토리 만들기
Pizza orderPizza(String type){
Pizza pizza;
if(type.equals("cheese")){
pizza = new CheesePizza();
} else if(type.equals("greek")){
pizza = new GreekPizza();
} else if(type.equals("pepperoni")){
pizza = new PepperoniPizza();
}
pizza.bake();
pizza.serve();
return pizza;
}
피자를 주문했을 때 어떤 피자를 주문했는지에 따라 피자객체를 생성하는 코드이다.
여기서 문제는 저 if-else if 구절이 다른 메소드에서도 쓰인다면 똑같이 if-else if를 그 쪽에도 사용을 해야하고 만약 새로운 피자가 생기거나 기존에 있던 피자가 사라진다면 저 if-else if 구절이 쓰인 곳을 일일히 찾아서 고쳐줘야 한다는 단점이 있다.
그렇다면 어떻게 해결하면 좋을까??
피자를 제공하는 Factory를 만들어보자
public class SimplePizzaFactory(){
public Pizza createPizza(String type){
Pizza pizza;
if(type.equals("cheese")){
pizza = new CheesePizza();
} else if(type.equals("greek")){
pizza = new GreekPizza();
} else if(type.equals("pepperoni")){
pizza = new PepperoniPizza();
}
return pizza;
}
}
이렇게 Pizza를 만드는 부분을 팩토리의 메소드로 넣어버린다면
public class PizzaStore{
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory){
this.factory = factory;
}
Pizza orderPizza(String type){
Pizza pizza;
factory.createPizza(type);
pizza.bake();
pizza.serve();
return pizza;
}
}
이렇게 if-elseif 구절이 있던 부분을 피자팩토리의 메소드를 호출함으로써 더 간단해졌다.
그럼 이번에는 좀 더 다양한 팩토리를 만들어보자
피자를 제공하는 피자 팩토리가 뉴욕스타일의 피자를 제공하는 곳이 있고 시카고스타일의 피자를 제공하는 곳이 있다고 생각해보자.
그러기 위해서는 두 피자스토어의 슈퍼클래스인 PizzaStore을 먼저 만들어 주어야 한다.
public abstract class PizzaStore{
public Pizza orderPizza(String type){
Pizza pizza;
pizza = createPizza(type);
pizza.bake();
pizza.serve();
return pizza;
}
abstract Pizza createPizza(String type);
}
뭔가 달라진 점이 있다.
바로 createPizza가 abstract 메소드가 됐다는 것!
그 이유는 뉴욕피자스토어와 시카고피자스토어에서 각자 다른 createPizza 메소드를 가질 것이기 때문이다.
PizzaStore의 서브클래스 뉴욕피자스토어와 시카고피자스토어를 만들어보자
public class NYPizzaStore extends PizzaStore{
@Override
Pizza createPizza(String item){
if(item.equals("cheese")){
return new NYCheesePizza();
} else if(item.equals("greek")){
return new NYGreekPizza();
} else if(item.equals("pepperoni")){
return new NYPepperoniPizza();
} else return null;
}
}
public class ChicagoPizzaStore extends PizzaStore{
@Override
Pizza createPizza(String item){
if(item.equals("cheese")){
return new ChicagoCheesePizza();
} else if(item.equals("greek")){
return new ChicagoGreekPizza();
} else if(item.equals("pepperoni")){
return new ChicagoPepperoniPizza();
} else return null;
}
}
바로 여기서 PizzaStore에서 abstract로 선언했던 createPizza method가 바로 팩토리 메소드이다.
abstract Product factoryMethod(String type);
abstract
팩토리 메소드를 추상 메소드로 선언해서 서브클래스(NYPizzaStore, ChicagoPizzaStore)가 객체 생성을 책임지도록 함Product
팩토리 메소드는 특정 객체를 리턴factoryMethod()
팩토리 메소드는 클라이언트(슈퍼클래스에 있는 orderPizza() 같은 코드)에서 실제로 어떤 구상 객체가 만들어지는지 모르게하는 역할도 함factoryMethod(String type)
팩토리 메소드를 만들 때 매개변수로 만들 객체 종류도 선택할 수 있음
그럼 이 코드들이 어떻게 사용되는지 보자public class PizzaTest{ public static void main(String[] args){ PizzaStore nyStore = new NYPizzaStore(); PizzaStore chicagoStroe = new ChicagoStore(); // NYCheesePizza가 담길 것이다. Pizza pizza = nyStore.orderPizza("cheese"); // ChicagoCheesePizza가 담길 것이다. Pizza pizza = chicagoStore.orderPizza("cheese"); } }
팩토리 메소드 패턴을 썼을 때의 장점
객체 생성 코드를 한 객체나 메소드에 넣으면 코드에서 중복되는 부분을 제거할 수 있고 나중에 관리할 때도 한 군데만 신경을 쓰면 된다.
또한 슈퍼클래스가 각 구상 클래스에 의존하지 않고 추상 클래스에만 의존하면 된다.
바로 마지막 장점을 의존성 뒤집기 원칙(Dependency Inversion Principle)이라고 한다.
추상화된 것에 의존하게 만들고 각 구상 클래스에는 의존하지 않게 한다는 뜻이다.
무슨 뜻이나면 만약 PizzaStore에서 직접 구상 클래스들을 만드는 예시를 보면public class PizzaStore{ public Pizza orderPizza(String type){ Pizza pizza; pizza = createPizza(type); pizza.bake(); pizza.serve(); return pizza; } Pizza createPizza(String type){ if(type.equals("NYCheese"){ return new NYCheesePizza(); } else if . . . }; }
이 때 PizzaStore은 피자의 구상 클래스인 NYCheesePizza, NYPepperoniPizza, ... etc 에 의존하게 된다.
하지만 위처럼 나타내게 되면 PizzaStore은 Pizza 추상 클래스 하나에만 의존하게되고 역으로 각각의 구상클래스들이 Pizza 추상클래스에 의존하게 된다.
이렇게 의존하는 방향이 바뀌어서 이것을 의존성 뒤집기 원칙이라고 한다.의존성 뒤집기 원칙을 지키는 방법
변수에 구상 클래스의 레퍼런스를 저장하지 않을 것 : new 연산자를 사용하면 구상 클래스의 레퍼런스를 사용하게 되므로 팩토리를 써서 구상 클래스를 변수에 저장하지 말자
구상 클래스에서 유도된 클래스를 만들지 않을 것 : 구상 클래스에서 유도된 클래스를 만들면 특정 구상 클래스에 의존하게 되니 인터페이스나 추상 클래스처럼 추상화된 것으로 클래스를 만들자
베이스 클래스에 이미 구현되어 있는 메소드를 오버라이드 하지 말자 : 이미 구현된 메소드를 오버라이드한다면 베이스 클래스가 제대로 추상화 되지 않음, 베이스 클래스에서 메소드를 정의할 때는 모든 서브클래스에서 공유할 수 있는 것만 정의
항상 이 원칙을 지켜야한다는 것은 아니고 지향할 바를 알려주는 것뿐이다!!
'백엔드 with java > Design Pattern' 카테고리의 다른 글
싱글톤(Singleton) feat. 스프링 (0) | 2023.12.04 |
---|---|
추상 팩토리 패턴(Abstract Factory Pattern) (0) | 2023.11.26 |
데코레이터 패턴(Decorator Pattern) (1) | 2023.11.23 |
옵저버 패턴(Observer Pattern) (1) | 2023.11.22 |
전략 패턴(Strategy Pattern) (0) | 2023.11.21 |