팩토리 패턴
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를 사용하세요. 이를 통해 코드가 더 유연해지고 새로운 제품 유형으로 확장하기가 더 쉬워집니다.
챌린지
중급이 챌린지에서는 상속과 다형성을 활용한 적절한 객체 지향 설계를 사용하여 도형 팩토리 시스템을 구현합니다.
다음 파일들의 구현을 완료하세요:
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")이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.