Menu
Coddy logo textTech

Паттерн Factory

Часть раздела Object Oriented Programming путешествия по Python на Coddy — урок 46 из 64.

Паттерн «Фабрика» создает объекты без указания их конкретного класса. Вместо прямого вызова конструкторов вы используете фабричный метод, который решает, какой класс инстанцировать.

Вот простые классы продуктов:

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 (20т)

Вывод:

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 — класс фабрики для создания фигур

Каждый файл содержит подробные комментарии 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}")

# Использование
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])  # Только марка
        elif vehicle_type == "truck":
            return Truck(args[0], args[1])  # Марка и грузоподъемность
        else:
            raise ValueError(f"Unknown type: {vehicle_type}")

# Использование
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