Menu
CoddyTech

Patron Factory

Fait partie de la section Programmation orientée objet du Journey Python de Coddy. Leçon 46 sur 64.

Le patron de fabrique crée des objets sans spécifier leur classe exacte. Au lieu d’appeler directement les constructeurs, vous utilisez une méthode de fabrique qui décide quelle classe instancier.

Voici des classes de produits simples :

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

Créez une classe fabrique pour produire ces objets :

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

Le code qui a besoin des objets est appelé le code client. Il s’adresse à la fabrique au lieu d’appeler directement les constructeurs :

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

print(my_car.info())   # Voiture : Toyota
print(my_bike.info())  # Moto : Honda

Rends la fabrique plus flexible en utilisant *args :

class FlexibleFactory:
    def create_vehicle(self, vehicle_type, *args):
        if vehicle_type == "car":
            return Car(args[0])  # Uniquement la marque
        elif vehicle_type == "truck":
            return Truck(args[0], args[1])  # Marque et capacité
        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)"

Utilisez la fabrique flexible :

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)

Résultat :

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

Point clé : le modèle de fabrique vous permet de créer des objets sans connaître leur classe exacte. La méthode de fabrique décide quelle classe instancier en fonction des paramètres. Utilisez *args pour gérer des produits ayant des paramètres de constructeur différents. Cela rend votre code plus flexible et plus facile à étendre avec de nouveaux types de produits.

challenge icon

Défi

Moyen

Dans ce défi, tu vas implémenter un système de fabrique de formes en utilisant une conception orientée objet appropriée avec l’héritage et le polymorphisme.

Complète l’implémentation dans les fichiers suivants :

  • shape.py - Classe Shape de base
  • circle.py - Implémentation de Circle
  • rectangle.py - Implémentation de Rectangle
  • triangle.py - Implémentation de Triangle
  • shapefactory.py - Classe fabrique pour créer des formes

Chaque fichier contient des commentaires TODO détaillés pour guider ton implémentation. Suis attentivement ces commentaires pour t’assurer que ton code respecte toutes les exigences.

Essayez vous-même

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

# Exécuteur de cas de test
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)
    
    # Récupérer les objets de méthode pour comparer les implémentations
    circle_area = Circle.area
    rectangle_area = Rectangle.area
    triangle_area = Triangle.area
    
    circle_perimeter = Circle.perimeter
    rectangle_perimeter = Rectangle.perimeter
    triangle_perimeter = Triangle.perimeter
    
    # Vérifier si toutes les implémentations sont distinctes
    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 iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Compilateur Python en ligne