코딩에 도움이 되는 요소/디자인 패턴

[디자인 패턴] 가상의 생성자를 두어서 관리하자!! 팩토리 메서드 패턴

너어디사니 2024. 5. 3. 19:50
  • 의도
  1. 가상의 생성자를 두어서 따로 관리하는 방법
  1. 부모 클래스에서 객체들을 생성할 수 있는 Interface를 제공하지만 자식 클래스들이 생성될 객체들의 유형을 변경할 수 있도록 하는 생성 패턴
  • 문제
    • 하나의 클래스이 모든 함수나 객체가 구현되어 있는 경우 추후에 많은 코드들이 추가 될 경우 추가 수정이 어려워짐은 물론 가독성이 떨어질 수도 있다
  • 구현 방법
  1. 모든 자식 클래스는 같은 Interface를 따르도록 설계를 진행한다 따라서 Interface는 모든 자식 클래스에서 의미가 있는 메서드를 선언한다
  1. Interface에 공통 함수에는 반환 값이 생성자 Interface를 받도록 구현을 한다
  1. 자식 클래스가 상속을 받으면서 생성자 Interface를 리턴 받을 수 있도록 구현한다
  • 장점
  1. Interface와 자식 클래스가 단단하게 결합되어있지 않아 보다 유연한 코드 구성이 가능하다
  1. 단일 책임 원칙의 자식 클래스 코드를 프로그램의 한 위치로 이동을 하여 코드를 조금 더 쉽게 유지 보수 할 수 있다
  1. 개방 / 폐쇄 원칙을 지키면서 기존의 클라이언트 코드를 훼손하지 않고 새로운 유형의 제품들을 프로그램에 도입할 수 있다
  • 단점
  1. 패턴을 구현하기 위해서 새로운 자식 클래스들을 도입해야 하므로 코드가 더 복잡해질 가능성이 있다
    - 자식 Interface들의 기존 계층구조의 패턴을 도입하는 것이 가장 좋다
  • 구조(회사 기준)
  1. 제품에 대한 Interface를 선언한다

interface Product {
    fun name()
}
  1. 원하는 제품을 Product Interface를 사용하여 구현해준다

class Music:Product {
    override fun name() {
        println("음악")
    }
}

// -----------------------------

class Video:Product {
    override fun name() {
        println("비디오")
    }
}
  1. 제품을 만드는 객체 반환하는 Interface를 생성해준다
- 이 때 만들 제품을 반환하는 함수를 만든다

interface Create {
    fun product(): Product
}
  1. 만들 제품을 클래스로 만들고 상속을 시켜준다

class CreateMusic:Create {
    private val music = Music()

    override fun product(): Product {
        return music
    }
}

// -----------------------------

class CreateVideo:Create {
    private val video = Video()
    override fun product(): Product {
        return video
    }
}

  • 클래스 다이어그램으로 보는 예시

 

  • 적용 예시
    • 코드가 함께 작동해야 하는 객체들의 정확한 유형들과 의존관계들을 미리 모르는 경우
      • 생성자 코드를 따로 분리하여 나머지 코드와는 독립적으로 확장하기 쉬워진다
    • 라이브러리 또는 프레임워크 사용자들에게 내부 컴포넌트들을 확장하는 방법을 제공하고 싶을 때 사용한다
      • 프레임워크 전체에서 컴포넌트들을 생성하는 코드를 단일 팩토리 메서드로 줄인 후에 누구나 이 펙토리 메서드를 Override를 할 수 있도록 한다
    • 기존 객체들을 매번 재구축하는 대신 이들을 재사용하여 시스템 리소스를 절약하고 싶을 때
      • 기존 객체를 재사용 하려면??
        1. 먼저 생성된 모든 객체를 추적하기 위해 일부 스토리지를 생성
        2. 누군가가 객체를 요청하면 프로그램은 해당 풀 내에서 Public 객체를 찾아야 한다
        3. … 이 객체를 클라이언트 코드에 반환
        4. Public 객체가 없으면, 프로그램은 새로운 객체를 생성 (그리고 풀에 이 객체를 추가)
      • 사용이 굉장히 복잡히지지만 팩토리 메서드 패턴을 사용하면 이러한 리소스를 최대한 절약하면서 구현할 수 있다