[ 디자인 패턴: 생성 ] (1) 팩토리 메서드 (Factory method)

2025. 3. 29. 17:10·개발
반응형


 

팩토리 메서드 : 부모 클래스에서 객체를 생성할 수 있는 인터페이스를 제공하고, 자식 클래스들은 생성 객체의 유형을 변경할 수 있도록 하는 생성 디자인 패턴

클라이언트에서 직접 new 연산자로 제품 객체를 생성하는 것이 아닌, 공장 클래스가 제품 객체들을 도맡아 생성한다.

서브 공장 클래스는 이를 상속하여 각각의 제품 객체 생성을 책임진다.

 


예제 1

 

interface Product {
    void setting();
}

class ProductA implements Product {
    public void setting() {}
}

class ProductB implements Product {
    public void setting() {}
}

 

위와 같은 제품 인터페이스와 제품 구현체가 있다고 해보자.

이제 위 제품을 찍어낼 공장이 필요하다!

 

interface AbstractFactory
{
    Product createProduct();

    default Product someOperation() {
        Product product = createProduct();
        product.setting();
        
        return product;
    }
}

class FactoryA implements AbstractFactory
{
    @Override
    public Product createProduct() {
        return new ProductA();
    }
}

class FactoryB implements AbstractFactory
{
    @Override
    public Product createProduct() {
        return new ProductB();
    }
}

 

제품을 찍어낼 공장 인터페이스 및 공장 구현체는 위와 같다.

메인 공장은 인터페이스 대신 추상 클래스를 사용해도 된다.

 

제품을 찍어내는 공장까지 만들었다!

이제 공장을 돌려서 제품을 만들어야 한다.

 

class Client
{
    public static void main(String[] args)
    {
        AbstractFactory[] factory = {
                new FactoryA(),
                new FactoryB()
        };

        Product productA = factory[0].createOperation();
        Product productB = factory[1].createOperation();
    }
}

 

클라이언트는 입맛에 맞게 자신이 원하는 제품을 찍어낼 수 있다!

 


예제 2

 

class Ship
{
    String name;
    String capacity;
}
class Client
{
    public static Ship orderShip(String name)
    {
        Ship ship = new Ship();
        ship.name = name;

        if (name.equalsIgnoreCase("ContainerShip")) {
            ship.capacity = "20t";
        } else if (name.equalsIgnoreCase("OilTankerShip")) {
            ship.capacity = "15t";
        } else {
            ship.capacity = "10t";
        }

        System.out.println("선박 주문이 완료되었습니다.");

        return ship;
    }

    public static void main(String[] args)
    {
        Ship containerShip = orderShip("ContainerShip");
        Ship oilTankerShip = orderShip("OilTankerShip");
    }
}

 

위의 예제를 보자.

클라이언트는 orderShip 메서드를 통해 선박을 주문한다.

이때, orderShip 메서드는 조건문을 통해 선박의 이름에 따라 선박의 용량을 결정한다.

 

이러한 코드는 문제가 있다.

선박의 종류가 계속 추가되면 조건문의 분기를 계속 추가해주어야 한다.

또한, Ship 클래스 구성 자체가 변한다면 분기문 로직을 통째로 바꿔주어야 한다.

 

class Ship
{
    String name;
    String capacity;
}

class ContainerShip extends Ship
{
    ContainerShip(String name, String capacity, String color) {
        this.name = name;
        this.capacity = capacity;
        this.color = color;
    }
}

class OilTankerShip extends Ship
{
    OilTankerShip(String name, String capacity, String color) {
        this.name = name;
        this.capacity = capacity;
        this.color = color;
    }
}
interface ShipFactory
{
    Ship createShip();
    
    default Ship orderShip()
    {
        Ship ship = createShip();
        
        System.out.println("선박 주문 완료!");
        
        return ship;
    }
}

class ContainerShipFactory implements ShipFactory
{
    @Override
    public Ship createShip() {
        return new ContainerShip("ContainerShip", "20t", "green");
    }
}

class OilTankerShipFactory implements ShipFactory
{
    @Override
    public Ship createShip() {
        return new OilTankerShip("OilTankerShip", "15t", "blue");
    }
}
class Client
{
    public static void main(String[] args)
    {
        Ship containerShip = new ContainerShipFactory().orderShip();
        Ship oilTankerShip = new OilTankerShipFactory().orderShip();
    }
}

 

위는 팩토리 메서드 패턴을 적용한 코드이다.

분기문을 통해 선박을 생성하는 대신, 선박 종류마다 전용 공장 객체를 통해 선박을 생성한다.

 

이를 통해, 확장성 있는 설계를 구현하였다!

새로운 종류의 선박이 추가되어도 기존의 코드를 수정하지 않아도 된다!

 


장점

 

  • 생성자와 구현 객체 간의 강한 결합을 피할 수 있다.
  • 팩토리 메서드를 통해, 객체들이 공통적으로 수행해야 하는 작업을 지정할 수 있다.
  • 캡슐화 및 추상화를 통해, 생성 객체의 구체적인 동작을 감출 수 있다.
  • 생성에 대한 인터페이스와 구현 부분을 따로 나누었기 때문에, 개별적으로 개발이 가능하다.
  • 단일 책임 원칙 (SRP) : 객체 생성 코드를 한 곳으로 이동하여 코드를 유지보수하기 쉽게 해준다.
  • 개방/폐쇄 원칙 (OCP) : 기존 코드를 수정하지 않고도 새로운 유형의 제품 객체를 도입할 수 있게 해준다.

 


단점

 

  • 각 제품 구현체마다 공장 객체를 모두 구현해줘야 하기 때문에, 구현체가 늘어날 때마다 공장 클래스가 증가하여, 서브 클래스 개수가 폭발할 수 있다.
  • 코드의 복잡성이 증가할 수 있다.

 


 

(참고)

https://refactoring.guru/ko/design-patterns/factory-method

https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9CFactory-Method-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90

 

 

반응형
저작자표시 비영리 변경금지 (새창열림)

'개발' 카테고리의 다른 글

[ 디자인 패턴: 생성 ] (3) 싱글톤 (Singleton)  (0) 2025.03.31
[ 디자인 패턴: 생성 ] (2) 추상 팩토리 (Abstract Factory)  (0) 2025.03.30
디자인 패턴이란?  (0) 2025.03.24
[ 김영한 스프링 강의 : 스프링 핵심 원리 - 기본편 ] 09. 빈 스코프  (0) 2025.03.12
[ 김영한 스프링 강의 : 스프링 핵심 원리 - 기본편 ] 08. 빈 생명주기 콜백  (1) 2025.03.08
'개발' 카테고리의 다른 글
  • [ 디자인 패턴: 생성 ] (3) 싱글톤 (Singleton)
  • [ 디자인 패턴: 생성 ] (2) 추상 팩토리 (Abstract Factory)
  • 디자인 패턴이란?
  • [ 김영한 스프링 강의 : 스프링 핵심 원리 - 기본편 ] 09. 빈 스코프
sleepzzz214
sleepzzz214
아! 응애에요~!
  • sleepzzz214
    응애 개발자의 일지
    sleepzzz214
  • 전체
    오늘
    어제
    • ⭐ (55)
      • 개발 (55)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    프로토타입
    객체 지향 프로그래밍
    의존 관계 주입
    상태
    스프링부트
    Solid
    싱글톤 패턴
    자바
    스프링 빈
    생성 패턴
    제어의 역전
    Spring
    의존성 주입
    상태 패턴
    state
    싱글톤
    java
    스프링 핵심 원리 - 기본편
    모니터링
    대규모 트래픽
    @Autowired
    스프링
    행동 패턴
    객체 지향 설계
    DI
    DB
    디자인 패턴
    데이터베이스
    구조 패턴
    김영한 스프링 강의
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
sleepzzz214
[ 디자인 패턴: 생성 ] (1) 팩토리 메서드 (Factory method)
상단으로

티스토리툴바