

팩토리 메서드 패턴 정리
DesignPattern
2025.10.17.
팩토리 메소드 패턴
1. 팩토리 메서드 패턴
구체적으로 어떤 인스턴스를 만들지는 서브 클래스가 정한다.
- 다양한 구현체(Product)가 있고, 그중에서 특정한 구현체(creator)를 만들 수 있는 다양한 팩토리를 제공할 수 있다.
- 객체 생성에 필요한 과정을 템플릿처럼 미리 구성해놓고, 객체 생성에 관한 전처리나 후처리를 통해 생성 과정을 다양하게 처리하여 객체를 유연하게 정할 수 있는 특징도 있다.
2. Simple Factory 패턴 소개, 코드
팩토리 메서드 패턴을 보기 전에 Simple 팩토리 메서드 패턴부터 살펴보고 가자
class Ship {
String name, color, capacity;
}
class ContainerShip extends Ship {
ContainerShip() {
name = "ContainerShip";
capacity = "20t";
color = "green";
}
}
class OilTankerShip extends Ship {
OilTankerShip() {
name = "OilTankerShip";
capacity = "15t";
color = "blue";
}
}
class ShipFactory {
final Ship orderShip(String name, String email) {
validate(name, email);
Ship ship = createShip(name); // 선박 객체 생성
sendEmailTo(email, ship);
return ship;
}
private Ship createShip(String name) {
Ship ship = null;
if (name.equalsIgnoreCase("ContainerShip")) {
ship = new ContainerShip();
} else if (name.equalsIgnoreCase("OilTankerShip")) {
ship = new OilTankerShip();
}
return ship;
}
private void validate(String name, String email) {
if (name == null) {
throw new IllegalArgumentException("배 이름을 지어주세요");
}
if (email == null) {
throw new IllegalArgumentException("이메일을 남겨주세요");
}
}
private void sendEmailTo(String email, Ship ship) {
System.out.println(ship.name + email);
}
}
- 객체 생성을 위한 로직을 가진 클래스를 따로 만들어, 클라이언트가 구체적인 클래스 이름 없이도 객체를 생성할 수 있게 하는 패턴이다.
- 나도 많이 사용하고 좋은 방식처럼 보이지만, 공장 클래스 내에서 여전히 분기 로직이 잔존해있다.
- 즉, 확장엔 열려있고, 수정엔 닫힌 개방-폐쇄 원칙을 만족하지 못하게 된다.
- 생성할 제품 구현체가 몇개 안되고 앞으로도 제품 종류가 추가되지 않는다면, 그냥 심플하게 심플 팩토리 메서드 패턴으로 구성하는 것도 나쁘지 않은 방법
3. 팩토리 메서드
- Creator : 최상위 공장 클래스로서, 팩토리 메서드를 추상화하여 서브 클래스로 하여금 구현하도로 함
- 팩토리 메서드(createProduct) : 서브 공장 클래스에서 재정의할 객체 생성 추상 메서드
- 객체 생성 처리 메서드(someOperartion) : 사진에는 없지만 Creator안에 존재한다. 객체 생성에 관한 전처리, 후처리를 템플릿화한 메소드이다.
- ConcreteCreator : 각 서브 공장 클래스들은 이에 맞는 제품 객체를 반환하도록 생성 추상 메소드를 재정의한다. 즉, 제품 객체 하나당 그에 걸맞는 생산 공장 객체가 위치된다.
- Product : 제품 구현체를 추상화
- ConcreteProduct : 제품 구현체
4. 팩토리 메서드 코드
class Ship {
String name, color, capacity;
}
public class BlackShip extends Ship {
public BlackShip() {
setName("BlackShip");
setLogo("Black");
setEmail("BlackLogo");
}
}
public class WhiteShip extends Ship {
public WhiteShip() {
setName("whiteShip");
setLogo("white");
setEmail("whiteLogo");
}
}
public interface ShipFactory {
default Ship orderShip(String name, String email) {
validate(name, email);
return createShip();
}
Ship createShip();
private void validate(String name, String email) {
//validate
if (Objects.isNull(name) || name.isBlank()) {
throw new IllegalArgumentException("배 이름을 지어주세요");
}
if (Objects.isNull(email) || email.isBlank()) {
throw new IllegalArgumentException("연락처를 남겨주세요");
}
}
}
public class BlackShipFactory implements ShipFactory {
@Override
public Ship createShip() {
return new BlackShip();
}
}
public class WhiteShipFactory implements ShipFactory {
@Override
public Ship createShip() {
return new WhiteShip();
}
}
class Client {
public static void main(String[] args) {
// 전용 선박 생산 공장 객체를 통해 선박을 생성
Ship containerShip = new BlackShipFactory().orderShip("devhshoon@gmail.com");
System.out.println(containerShip);
Ship oilTankerShip = new WhiteShipFactory().orderShip("devhshoon@gmail.com");
System.out.println(oilTankerShip);
}
}
코드 작성 팁 정리
- Factory를 interface로 구현
- 배를 직접적으로 생성시키는 메소드만 추상 메소드로 정의
- default 메서드를 통해 interface내에서 추상 메소드를 상속받는 모든 구현체에서 필요한 기능을 구현 (someOperartion)
- 자바 8 이후부터는, 기본 메소드를 정의할 수 있기 때문에, 추상 메소드를 상속받는 모든 구현체에서 필요한 기능을 상속받을 수 있도록 되었다.
- 배를 만들어 반환하는 메서드를 구현하는 구현체를 각각 책임에 맞게 정의
- 클라이언트에서 해당하는 구현체를 선택
- 선박 타입명을 입력값으로 줘서 분기문을 통해 선박 제품을 생성하는 것이 아닌, 전용 선박 생산 공장 객체를 통해 선박을 생성함으로써, 수정에 닫혀있고 확장에 열려있는 구조를 구성할 수 있게 된다.
- 만일 BattleShip 이라는 새로운 종류의 선박 제품을 추가한다라고 가정하면, 간단하게 제품 객체와 공장 객체를 각각 정의하고 상속 시키기만 하면 기존에 작성 했던 코드 수정없이 확장된다.
5. 팩토리 메서드 사용 시기, 장, 단점
-
사용 시기
- 클래스 생성과 사용의 처리 로직을 분리하여 결합도를 낮추고자 할 때
- 코드가 동작해야 하는 객체의 유형과 종속성을 캡슐화를 통해 정보 은닉 처리 할 경우
- 라이브러리 혹은 프레임워크 사용자에게 구성 요소를 확장하는 방법을 제공하려는 경우
- 기존 객체를 재구성하는 대신 기존 객체를 재사용하여 리소스를 절약하고자 하는 경우
-
팩토리 메소드 패턴을 적용했을 때의 장, 단점
- 장점
- 요구사항의 변경에도 기존코드를 변경하지 않고 확장하여 처리할 수 있는 보기좋은 구조가 된다.
- 생성자(Creator)와 구현 객체(concrete product)의 강한 결합을 피함
- 팩토리 메서드를 통해 객체의 생성 후 공통으로 할 일을 수행하도록 지정 가능
- 캡슐화, 추상화를 통해 생성되는 객체의 구체적인 타입을 감출수 있다.
- 단일 책임 원칙 준수 : 객체 생성 코드를 한 곳(패키지, 클래스 등) 으로 이동하여 코드를 유지보수하기 쉽게 하기에
- 개방 / 폐쇄 원칙 준수 : 기존 코드를 수정하지 않고 새로운 유형의 제품 인스턴스를 프로그램에 도입할 수 있어 원칙을 만족 (확장성 있는 전체 프로젝트 구성이 가능)
- 단점
- 각자의 역할을 나누다 보니 클래스가 증가한다.
- 장점
6. 심플 팩토리 메서드 패턴 vs 팩토리 메서드 패턴
- 심플 팩토리
- 생성해야 할 객체의 종류가 많지 않고, 앞으로도 거의 변경되지 않을 때
- 간단하게 객체 생성 코드를 한 곳에 모아 분리하고 싶을 때
- 팩토리 메서드 패턴
- 어떤 객체를 생성할지 미리 알 수 없고, 런타임에 서브클래스가 결정해야 할 때
- 프레임워크나 라이브러리처럼 확장성을 중요하게 고려해야 할 때
- 새로운 종류의 객체가 자주 추가될 가능성이 높을 때
팩토리 메소드 패턴의 목적은 변경에는 닫혀있고, 확장에는 열려있는 구조를 만드는 것
즉, OCP 원칙을 지키기 위한 패턴에 가깝다.
- 클라이언트 코드는 바뀔 수 있지 않냐?
- 기존 product와 factory가 변경되지 않는다.
다음을 직접 설명해라
1. “확장에 열려있고 변경에 닫혀있는 객체 지향 원칙”(OCP)을 설명하세요.
-> 변경에 닫혀있다. -> 기존 코드를 변경하지 않는다.
-> 확장에 열려있다. -> 새로운 기능을 확장할 수 있다.
2. 자바 8에 추가된 default 메소드에 대해 설명하세요.
-> 인터페이스에서 기본적인 구현체를 만들 수 있다. (이전에는 추상 메서드만 만들 수 있었다.)
-> 자바 9에서는 private 메서드를 정의할 수 있다.
7. 팩토리 패턴을 싱글톤화
객체를 생성하는 공장 객체는 여러개 있을 필요성이 없고, 싱글톤 객체로 구성하여 유일한 인스턴스로 만들어 두는 것이 좋은 방법이다.
public class BlackShipFactory extends ShipFactory {
private BlackShipFactory () {};
private static class BlackShipFactoryHolder () {
private static final BlackShipFactory INSTANCE = new BlackShipFactory();
}
public static BlackShipFactory getInstance() {
return BlackShipFactoryHolder.INSTANCE;
}
@Override
public Ship createShip () {
return new BlackShip();
}
}
public class WhiteShipFactory implements ShipFactory {
private WhiteShipFactory () {};
private static class WhiteShipFactoryHolder {
private static final WhiteShipFactory INSTANCE = new WhiteShipFactory();
}
public static WhiteShipFactory getInstance() {
return WhiteShipFactoryHolder.INSTANCE;
}
@Override
public Ship createShip() {
return new WhiteShip();
}
}
class Client {
public static void main(String[] args) {
Ship blackShip = BlackShipFactory.getInstance().orderShip("devhshoon@gmail.com");
System.out.println(blackShip);
Ship whiteShip = WhiteShipFactory.getInstance().orderShip("devhshoon@gmail.com");
System.out.println(whiteShip);
}
}
8. 자바와 스프링에서 찾아보는 패턴
- 단순한 팩토리 패턴
- 매개변수의 값에 따라 또는 메소드에 따라 각기 다른 인스턴스를 리턴하는 단순한 버전의 팩토리 메서드 패턴
- ex - java.lang.Calendar 또는 java.lang.NumberFormat
- Calendar는 Gregorian 형식 Julian 형식이 있는데, 이 두가지 경우를 모두 커버하기 위해 팩토리 메소드 패턴으로 디자인 되었다.
- 국가 또는 화폐에 따라 다른 표현 방식을 커버하기 위해 팩토리 메소드 패턴으로 디자인
- NumberFormat을 구현하는 클래스는 DecimalFormat, ExponentialFormat 등
- 스프링의 BeanFactory
- Object타입의 Product를 만드는 BeanFactory라는 Creator
Reference
코딩으로 학습하는 GoF의 디자인 패턴 - 백기선 팩토리 메서드(Factory Method) 패턴 - 완벽 마스터하기 - Inpa Dev