Menu
Coddy logo textTech

팩토리 패턴

Coddy Python 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 64개 중 46번째.

팩토리 패턴(Factory Pattern)은 정확한 클래스를 지정하지 않고 객체를 생성합니다. 생성자를 직접 호출하는 대신, 어떤 클래스를 인스턴스화할지 결정하는 팩토리 메서드를 사용합니다.

다음은 간단한 제품 클래스들입니다:

class Car:
    def __init__(self, brand):
        self.brand = brand
        self.type = "Car"
    
    def info(self):
        return f"{self.type}: {self.brand}"

class Bike:
    def __init__(self, brand):
        self.brand = brand
        self.type = "Bike"
    
    def info(self):
        return f"{self.type}: {self.brand}"

이러한 객체들을 생성하기 위한 팩토리 클래스를 생성하세요:

class VehicleFactory:
    def create_vehicle(self, vehicle_type, brand):
        if vehicle_type == "car":
            return Car(brand)
        elif vehicle_type == "bike":
            return Bike(brand)
        else:
            raise ValueError(f"Unknown type: {vehicle_type}")

생성자를 직접 호출하는 대신 팩토리를 사용하세요:

factory = VehicleFactory()
my_car = factory.create_vehicle("car", "Toyota")
my_bike = factory.create_vehicle("bike", "Honda")

print(my_car.info())   # 자동차: Toyota
print(my_bike.info())  # 자전거: Honda

*args를 사용하여 팩토리를 더 유연하게 만드세요:

class FlexibleFactory:
    def create_vehicle(self, vehicle_type, *args):
        if vehicle_type == "car":
            return Car(args[0])  # 브랜드만
        elif vehicle_type == "truck":
            return Truck(args[0], args[1])  # 브랜드와 적재 용량
        else:
            raise ValueError(f"Unknown type: {vehicle_type}")

class Truck:
    def __init__(self, brand, capacity):
        self.brand = brand
        self.capacity = capacity
        self.type = "Truck"
    
    def info(self):
        return f"{self.type}: {self.brand} ({self.capacity}t)"

유연한 팩토리를 사용하세요:

flexible = FlexibleFactory()
car = flexible.create_vehicle("car", "Ford")
truck = flexible.create_vehicle("truck", "Volvo", "20")

print(car.info())    # 자동차: Ford
print(truck.info())  # 트럭: Volvo (20t)

출력:

Car: Toyota
Bike: Honda
Car: Ford
Truck: Volvo (20t)

핵심 포인트: 팩토리 패턴(Factory Pattern)을 사용하면 정확한 클래스를 알지 못해도 객체를 생성할 수 있습니다. 팩토리 메서드는 매개변수를 기반으로 어떤 클래스를 인스턴스화할지 결정합니다. 서로 다른 생성자 매개변수를 가진 제품들을 처리하려면 *args를 사용하세요. 이를 통해 코드가 더 유연해지고 새로운 제품 유형으로 확장하기가 더 쉬워집니다.

challenge icon

챌린지

중급

이 챌린지에서는 상속과 다형성을 활용한 적절한 객체 지향 설계를 사용하여 도형 팩토리 시스템을 구현합니다.

다음 파일들의 구현을 완료하세요:

  • shape.py - 기본 Shape 클래스
  • circle.py - Circle 구현
  • rectangle.py - Rectangle 구현
  • triangle.py - Triangle 구현
  • shapefactory.py - 도형을 생성하는 Factory 클래스

각 파일에는 구현을 안내하는 상세한 TODO 주석이 포함되어 있습니다. 코드가 모든 요구 사항을 충족할 수 있도록 이 주석들을 주의 깊게 따라주세요.

치트 시트

팩토리 패턴(Factory Pattern)은 정확한 클래스를 명시하지 않고 객체를 생성합니다. 생성자를 직접 호출하는 대신, 어떤 클래스를 인스턴스화할지 결정하는 팩토리 메서드를 사용합니다.

기본적인 팩토리 구현:

class VehicleFactory:
    def create_vehicle(self, vehicle_type, brand):
        if vehicle_type == "car":
            return Car(brand)
        elif vehicle_type == "bike":
            return Bike(brand)
        else:
            raise ValueError(f"Unknown type: {vehicle_type}")

# Usage
factory = VehicleFactory()
my_car = factory.create_vehicle("car", "Toyota")
my_bike = factory.create_vehicle("bike", "Honda")

서로 다른 생성자 매개변수를 위해 *args를 사용하는 유연한 팩토리:

class FlexibleFactory:
    def create_vehicle(self, vehicle_type, *args):
        if vehicle_type == "car":
            return Car(args[0])  # Just brand
        elif vehicle_type == "truck":
            return Truck(args[0], args[1])  # Brand and capacity
        else:
            raise ValueError(f"Unknown type: {vehicle_type}")

# Usage
flexible = FlexibleFactory()
car = flexible.create_vehicle("car", "Ford")
truck = flexible.create_vehicle("truck", "Volvo", "20")

주요 장점: 팩토리 패턴은 기존 클라이언트 코드를 수정하지 않고도 새로운 제품 유형으로 확장하기 쉽고 코드를 더 유연하게 만듭니다.

직접 해보기

from shapefactory import ShapeFactory
from shape import Shape
from circle import Circle
from rectangle import Rectangle
from triangle import Triangle
import sys

# 테스트 케이스 실행기
test_case = input()

factory = ShapeFactory()

if test_case == "circle_area":
    circle = factory.create_shape("circle", 5)
    print(f"{circle.area():.2f}")

elif test_case == "rectangle_perimeter":
    rectangle = factory.create_shape("rectangle", 4, 6)
    print(f"{rectangle.perimeter()}")

elif test_case == "triangle_perimeter":
    triangle = factory.create_shape("triangle", 3, 4, 5)
    print(f"{triangle.perimeter()}")

elif test_case == "invalid_shape":
    try:
        factory.create_shape("hexagon", 6)
        print("No exception raised")
    except ValueError as e:
        print(str(e))

elif test_case == "case_insensitive":
    circle = factory.create_shape("CiRcLe", 3)
    print(f"{circle.area():.2f}")

elif test_case == "shape_inheritance":
    shapes = [
        factory.create_shape("circle", 2),
        factory.create_shape("rectangle", 2, 3),
        factory.create_shape("triangle", 3, 4, 5)
    ]
    all_shapes = all(isinstance(shape, Shape) for shape in shapes)
    print(all_shapes)

elif test_case == "zero_radius_circle":
    circle = factory.create_shape("circle", 0)
    print(f"{circle.area():.2f} {circle.perimeter():.2f}")

elif test_case == "negative_dimensions":
    rectangle = factory.create_shape("rectangle", -2, -3)
    print(f"{rectangle.area()}")

elif test_case == "large_values":
    circle = factory.create_shape("circle", 1000000)
    print(f"{circle.area():.2e}")

elif test_case == "polymorphism_test":
    shapes = [
        factory.create_shape("circle", 2),
        factory.create_shape("rectangle", 3, 4),
        factory.create_shape("triangle", 3, 4, 5)
    ]
    area_sum = sum(shape.area() for shape in shapes)
    perimeter_sum = sum(shape.perimeter() for shape in shapes)
    print(f"Area sum: {area_sum:.2f}, Perimeter sum: {perimeter_sum:.2f}")

elif test_case == "triangle_area":
    triangle = factory.create_shape("triangle", 3, 4, 5)
    print(f"{triangle.area():.2f}")

elif test_case == "method_override":
    circle = factory.create_shape("circle", 2)
    rectangle = factory.create_shape("rectangle", 3, 4)
    triangle = factory.create_shape("triangle", 3, 4, 5)
    
    # 구현을 비교하기 위해 메서드 객체 가져오기
    circle_area = Circle.area
    rectangle_area = Rectangle.area
    triangle_area = Triangle.area
    
    circle_perimeter = Circle.perimeter
    rectangle_perimeter = Rectangle.perimeter
    triangle_perimeter = Triangle.perimeter
    
    # 모든 구현이 고유한지 확인
    unique_areas = len({circle_area, rectangle_area, triangle_area}) == 3
    unique_perimeters = len({circle_perimeter, rectangle_perimeter, triangle_perimeter}) == 3
    
    if unique_areas and unique_perimeters:
        print("All shapes correctly override methods")
    else:
        print("Some shapes share method implementations")
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

Object Oriented Programming의 모든 레슨