본문 바로가기
프로그래밍/디자인패턴

팩토리 메서드(Factory Method) 패턴

by bantomak 2024. 4. 23.

팩토리 메서드

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

 

 

팩토리 메서드는 기존 객체들을 매번 재구축하는 대신 이들을 재사용하여 시스템 리소스를 절약하고 싶을 때 사용하자.

 

이러한 요구 사항은 데이터베이스 연결, 파일 시스템 및 네트워크처럼 시스템 자원을 많이 사용하는 대규모 객체들을 처리할 때 자주 발생한다. 기존 객체를 재사용하려면 무엇을 해야 하는지 한번 생각해 보자.

 

  1. 먼저 생성된 모든 객체를 추적하기 위해 일부 스토리지를 생성해야 한다.
  2. 누군가가 객체를 요청하면 프로그램은 해당 풀 내에서 유휴(free) 객체를 찾아야 한다.
  3. 그 후 이 객체를 클라이언트 코드에 반환해야 한다.
  4. 유휴 객체가 없으면, 프로그램은 새로운 객체를 생성해야 한다. (그리고 풀에 이 객체를 추가해야 한다.)

이것은 정말로 많은 양의 코드이다. 그리고 프로그램을 중복 코드로 오염시키지 않도록 이 많은 양의 코드를 모두 한 곳에 넣어야 한다. 

 

아마도 이 코드를 배치할 수 있는 가장 확실하고 편리한 위치는 우리가 재사용하려는 객체들의 클래스의 생성자일 것이다. 그러나 생성자는 특성상 항상 새로운 객체들을 반환해야 하며, 기존 인스턴스를 반환할 수 없다.

 

따라서 새 객체들을 생성하고 기존 객체를 재사용할 수 있는 일반적인 메서드가 필요하다.

 

의사코드

class Dialog is
    // 크리에이터는 팩토리 메서드의 일부 디폴트 구현을 제공할 수 있다.
    abstract method createButton():Button
    
    method rendor() is
        // 팩토리 메서드를 호출하여 제품 객체를 생성
        Button okButton = CreateButton()
        
        okButton.OnClick(closeDialog)
        okButton.render()
        
 class WindowDialog extends Dialog is
     method createButton():Button is
         return new WindowButton()
         
 class WebDialog extends Dialog is
     method createButton():Button is
         return new HTMLButton()
         
 interface Button is
     method render()
     method onClick(f)
     
 class WindowsButton implements Button is
     method render(a, b) is
     method onClick(f) is
     
 class HTMLButton implements Button is
     method render(a, b) is
     method onClick(f) is
     
 class Application is
     field dialog: Dialog
     
     method initialize() is
         config = readApplicationConfigFile()
     
         if (config.OS == "Window") then
             dialog = new WindowDialog()
         else if (config.OS == "Web") then
             dialog = new WebDialog()
         else
            throw new Exception("Error! Unknown operationg system.")
         
     method main() is
         this.initialize()
         dialog.render()

⚡ 구현 방법

  1. 모든 제품이 같은 인터페이스를 따르도록 하라. 이 인터페이스는 모든 제품에서 의미가 있는 메서드들을 선언해야 한다.
  2. 크리에이터 클래스 내부에 빈 팩토리 메서드를 추가하자. 이 메서드의 반환 유형은 공통 제품 인터페이스와 일치해야 한다.
  3. 크리에이터의 코드에서 제품 생성자들에 대한 모든 참조를 찾자. 이 참조들을 하나씩 팩토리 메서드에 대한 호출로 교체하면서 제품 생성 코드를 팩토리 메서드로 추출하자.
  4. 이제 팩토리 메서드에 나열된 각 제품 유형에 대한 크리에이터 자식 클래스들의 집합을 생성한 후, 자식 클래스들에서 팩토리 메서드를 오버라이딩하고 기초 메서드에서 생성자 코드의 적절한 부분들을 추출하자.
  5. 제품 유형이 너무 많아 모든 제품에 대하여 자식 클래스들을 만드는 것이 합리적이지 않을 경우, 자식 클래스들의 기초 클래스의 제어 매개변수를 재사용할 수 있다.
  6. 추출이 모두 끝난 후 기초 팩토리 메서드가 비어 있으면, 해당 팩토리 메서드를 추상화할 수 있다. 팩토리 메서드가 비어있지 않으면, 나머지를 그 메서드의 디폴트 행동으로 만들 수 있다.

👍 장단점

  • 크리에이터와 구상 제품들이 단단하게 결합되지 않도록 할 수 있다.
  • 단일 책임 원칙. 제품 생성 코드를 프로그램의 한 위치로 이동하여 코드를 더 쉽게 유지관리 할 수 있다.
  • 개방 폐쇄 원칙. 기존 클라이언트 코드를 훼손하지 않고 새로운 유형의 제품들을 프로그램에 도입할 수 있다.
  • 패턴을 구현하기 위해 많은 새로운 자식 클래스들을 도입해야하므로 코드가 더 복잡해질 수 있다. 가장 좋은 방법은 크리에이터 클래스들의 기존 계층구조에 패턴을 도입하는 것이다.

🔍다른 패턴과의 관계

  • 많은 디자인은 복잡성이 낮고 자식 클래스들을 통해 더 많은 커스터마이징이 가능한 팩토리 메서드로 시작해 더 유연하면서도 더 복잡한 추상 팩토리, 프로토타입 또는 빌더 패턴으로 발전해 나간다.
  • 추상 팩토리 클래스들은 팩토리 메서드들의 집합을 기반으로 하는 경우가 많다. 그러나 또한 프로토타입을 사용하여 추상 팩토리의 구상 클래스들의 생성 메서드들을 구현할 수도 있다.
  • 팩토리 메서드반복자와 함께 사용하여 컬렉션 자식 클래스들이 해당 컬렉션들과 호환되는 다양한 유형의 반복자들을 반환하도록 할 수 있다.
  • 프로토타입은 상속을 기반으로 하지 않으므로 상속과 관련된 단점들이 없다. 반면에 프로토타입은 복제된 객체의 복잡한 초기화가 필요하다. 팩토리 메서드는 상속을 기반으로 하지만 초기화 단계가 필요하지 않다.
  • 팩토리 메서드템플릿 메서드의 특수화라고 생각할 수 있다. 동시에 대규표 템플릿 메서드의 한 단계의 역할을 팩토리 메서드가 할 수 있다.

정리하자면

이미 정의되어 있는 메서드에 행위는 정의되어 있고 이에 대한 세부 행동을 구체 클래스에서 정의한다.

 

참고 사이트

 

리팩터링과 디자인 패턴

Hello, world! Refactoring.Guru는 리팩토링, 디자인 패턴, SOLID 원칙 및 기타 스마트 프로그래밍 주제에 대해 알아야 할 모든 것을 쉽게 찾을 수 있는 자원입니다. 이 사이트에서는 이러한 모든 주제가 어

refactoring.guru

댓글