소프트웨어 개발자/디자인패턴

[디자인패턴] 생성 패턴 - 팩토리 (Factory) 패턴

yubi5050 2024. 3. 30. 15:57

생성 (Creational) 패턴

다양한 상황에서의 객체의 생성 방식을 결정하는 디자인 패턴

 

주요 패턴으론 다음 사항들이 있다.

  • 팩토리 메서드 (Factory Method) <- 이번 글에서 다룰 내용
  • 추상 팩토리 (Abstract Factory) <- 이번 글에서 다룰 내용
  • 빌더(Builder) 
  • 싱글톤(SingleTon) 
  • 프로토타입(Prototype)  
  • 템플릿메서드(TemplateMethod)

 

팩토리 패턴 이란? 

  • 생성 패턴의 한 방법으로, 여러 객체들을 팩토리로 캡슐화를 통해 다양한 종류의 객체들을 생성 구현 하는 것
  • 팩토리(추상) 클래스에 각 객체를 생성 해주는 로직들을 캡슐화 하고 Argument 호출 등을 통해, 가독성을 높인다.
  • 팩토리 패턴은 조건에 해당되는 '생성된 객체'를 리턴해 주는 것이 핵심
  • 주로 클래스의 생성자의 로직이 복잡해졌을 때 추상화 레벨로 올려 사용함
  • 주로 Factory, Creator 와 같은 클래스/함수명이 많이 붙음

 

추상 팩토리 (Abstract Factory) 패턴 이란?

추상 팩토리 (Abstract Factory)는 일반적인 팩토리 패턴보다 좀더 확장한 개념으로, 공통 테마의 여러 팩토리들을 1개의 그룹으로 캡슐화 하여, 관련 객체의 패밀리를 생성하는 방법

 

팩토리 메소드 패턴 vs 추상 팩토리  패턴

팩토리 메소드 패턴

- 최종 하나의 객체를 생성하는데 사용됨 

- ex) (Cat, Dog) → AnimalFactory (Cat/Dog 중 한 가지 동물 생성)

- 쉽게 말해, 다양한 input에 따라 객체 생성이 달라지는 방식

- 구상 클래스의 인스턴스 생성을 서브클래스에서 결정

- 메소드 레벨의 포커스

 

추상 팩토리 패턴

- 팩토리 메서드 패턴의 보다 일반화 버전 

- 여러 팩토리들이 가진 다양한 객체(그룹화)를 생성하기 위해 사용

- ex) AnimalWorldFactory(Cat 생성, Dog 생성 포함), InsectWorldFactory(Larva 생성, Butterfly 생성) → AnimalWorld, InsectWorld → World 중 하나를 생성

- 여러 객체에 대한 생성을 하나의 팩토리 클래스로 묶고 그 팩토리 클래스(그룹) 단위로 사용하는 것

 

 

예시

- 다음과 같이 제품 객체 (Chair, Sofa, Coffeetable) 등과 여러 변형 방법 스타일 (ArtDeco, Victorian, Modern) 들이 있으며, 고객 들은 비슷한 스타일의 제품을 원할 수 있고, 공장은 새 제품을 추가할 때, 기존 변형 방법 유형(코드)를 수정하길 원하지 않는다.

 

https://refactoring.guru/design-patterns/abstract-factory

 

팩토리 패턴은 FurnitureFactory (Chair, Sofa, CoffeeTable 중 하나를 생성) 를 만드는 것

추상 팩토리 패턴은 FurnitureFactory (ChairFactory, SofaFactory, CoffeeTableFactory 중 하나를 생성) 를 만드는 것

ChairFactory, SofaFactory, CoffeeTableFactory 들은 다양한 변형을 적용한 객체를 생성

각 Factory들은 클라이언트들에게 Interface로 제공되어 사용됨

 

예시 코드

예시코드 1

- 팩토리 메서드 

from __future__ import annotations
from abc import ABC, abstractmethod

# 추상클래스
class Animal(ABC):
    # 추상 메소드는 반드시 구현 해야 함, 정형화 목적으로 사용
    @abstractmethod
    def speak(self):
        pass

# 객체1
class Dog(Animal):
    def speak(self):
        print('Dog')

# 객체2
class Cat(Animal):
    def speak(self):
        print('Cat')

# 객체1과 객체2를 중간에서 생성해주는 팩토리
class AnimalFactory():
    def create_animal(self, type:str):
        if type=='Dog':
            return Dog()
        elif type=='Cat':
            return Cat()


if __name__ == "__main__":
    # 강아지와 고양이를 하나씩 만들어서 짖게함.
    dog = Dog()
    dog.speak()

    cat = Cat()
    cat.speak()

    # 같은 클래스의 인자만 조절하고 싶다.
    animal_factory = AnimalFactory()
    animal_factory.create_animal(type='Cat').speak()

 

예시코드 2

- 팩토리 메서드 기본

class JSONExtractor:
    def __init__(self, file_path):
        with open(file_path, encoding='utf-8') as f:
            self.data = json.load(f)

class XMLExtractor:
    def __init__(self, file_path):
        with open(file_path, encoding='utf-8') as f:
            self.data = json.load(f)

def extraction_factory(file_path):
    if file_path.endswith('json'):
        extractor = JSONExtractor
    elif file_path.endswith('xml'):
        extractor = XMLExtractor
    return extractor(file_path)

 

장단점

장점

  • 팩토리에서 만든 제품과 다른 제품들끼리 상호호환을 보장
  • 클라이언트 코드와 구현체의 강한 결합을 피할 수 있음
  • SRP : 제품 생성 코드를 추출해서 한 곳으로 옮길 수 있다. 유지보수에 좋음
  • OCP : 기존 클라이언트 코드를 break하지 않고, 추가 가능

 

단점

  • 패턴 구현을 위해, 많은 클래스와 인터페이스들이 필요하기 때문에, 복잡도가 올라감

 

 

 

참고 링크

- 추상 팩토리(Abstract Factory) 패턴 - 완벽 마스터하기

- 실용적인 Python 디자인 패턴 정리