Menu
CoddyTech

Wzorzec Factory

Część sekcji Programowanie obiektowe ścieżki Python w Coddy. Lekcja 46 z 64.

Wzorzec Fabryki tworzy obiekty bez określania ich dokładnej klasy. Zamiast bezpośrednio wywoływać konstruktory, używasz metody fabrykującej, która decyduje, którą klasę utworzyć.

Oto proste klasy produktów:

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}"

Utwórz klasę fabryki do tworzenia tych obiektów:

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}")

Kod, który potrzebuje obiektów, nazywa się kodem klienta. Zwraca się do fabryki, zamiast bezpośrednio wywoływać konstruktory:

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

print(my_car.info())   # Samochód: Toyota
print(my_bike.info())  # Motocykl: Honda

Spraw, aby fabryka była bardziej elastyczna, używając *args:

class FlexibleFactory:
    def create_vehicle(self, vehicle_type, *args):
        if vehicle_type == "car":
            return Car(args[0])  # Tylko marka
        elif vehicle_type == "truck":
            return Truck(args[0], args[1])  # Marka i ładowność
        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)"

Użyj elastycznej fabryki:

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

print(car.info())    # Car: Ford
print(truck.info())  # Truck: Volvo (20t)

Wynik:

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

Najważniejsza kwestia: wzorzec fabryki pozwala tworzyć obiekty bez znajomości ich dokładnej klasy. Metoda fabrykująca decyduje na podstawie parametrów, którą klasę utworzyć. Użyj *args, aby obsługiwać produkty z różnymi parametrami konstruktora. Dzięki temu Twój kod jest bardziej elastyczny i łatwiejszy do rozszerzania o nowe typy produktów.

challenge icon

Wyzwanie

Średni

W tym wyzwaniu zaimplementujesz system fabryki kształtów, stosując poprawny projekt obiektowy z dziedziczeniem i polimorfizmem.

Uzupełnij implementację w następujących plikach:

  • shape.py — klasa bazowa Shape
  • circle.py — implementacja Circle
  • rectangle.py — implementacja Rectangle
  • triangle.py — implementacja Triangle
  • shapefactory.py — klasa fabryki tworząca kształty

Każdy plik zawiera szczegółowe komentarze TODO, które pomogą Ci w implementacji. Postępuj zgodnie z tymi komentarzami, aby mieć pewność, że Twój kod spełnia wszystkie wymagania.

Spróbuj swoich sił

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

# Wykonywanie przypadków testowych
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)
    
    # Pobierz obiekty metod, aby porównać implementacje
    circle_area = Circle.area
    rectangle_area = Rectangle.area
    triangle_area = Triangle.area
    
    circle_perimeter = Circle.perimeter
    rectangle_perimeter = Rectangle.perimeter
    triangle_perimeter = Triangle.perimeter
    
    # Sprawdź, czy wszystkie implementacje są unikalne
    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 iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Python online