Menu
CoddyTech

Mẫu Factory

Thuộc phần Lập trình hướng đối tượng trong hành trình Python của Coddy. Bài học 46 trên 64.

Mẫu Factory (Factory Pattern) tạo đối tượng (object) mà không cần chỉ định lớp (class) cụ thể. Thay vì gọi trực tiếp các hàm khởi tạo (constructor), bạn dùng phương thức factory (factory method) để quyết định cần khởi tạo lớp nào.

Đây là các lớp sản phẩm đơn giản:

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

Tạo một lớp factory để tạo ra các đối tượng này:

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

Code cần các đối tượng được gọi là code phía client. Code này yêu cầu factory thay vì gọi trực tiếp các hàm khởi tạo:

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

print(my_car.info())   # Ô tô: Toyota
print(my_bike.info())  # Xe máy: Honda

Làm cho factory linh hoạt hơn bằng cách dùng *args:

class FlexibleFactory:
    def create_vehicle(self, vehicle_type, *args):
        if vehicle_type == "car":
            return Car(args[0])  # Chỉ có thương hiệu
        elif vehicle_type == "truck":
            return Truck(args[0], args[1])  # Thương hiệu và tải trọng
        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)"

Sử dụng nhà máy linh hoạt:

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

print(car.info())    # Xe con: Ford
print(truck.info())  # Xe tải: Volvo (20t)

Đầu ra:

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

Điểm chính: Mẫu nhà máy cho phép bạn tạo đối tượng mà không cần biết chính xác lớp của chúng. Phương thức nhà máy quyết định sẽ khởi tạo lớp nào dựa trên các tham số. Dùng *args để xử lý các sản phẩm có tham số hàm khởi tạo khác nhau. Nhờ vậy, code của bạn linh hoạt hơn và dễ mở rộng với các loại sản phẩm mới.

challenge icon

Thử thách

Trung bình

Trong thử thách này, bạn sẽ triển khai một hệ thống factory hình dạng bằng cách áp dụng thiết kế hướng đối tượng phù hợp với kế thừa và đa hình.

Hoàn thành phần triển khai trong các tệp sau:

  • shape.py - Lớp Shape cơ sở
  • circle.py - Phần triển khai Circle
  • rectangle.py - Phần triển khai Rectangle
  • triangle.py - Phần triển khai Triangle
  • shapefactory.py - Lớp factory để tạo hình dạng

Mỗi tệp đều có các chú thích TODO chi tiết để hướng dẫn bạn triển khai. Hãy đọc kỹ các chú thích này để đảm bảo code đáp ứng mọi yêu cầu.

Tự làm thử

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

# Chạy test case
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)
    
    # Lấy các đối tượng phương thức (method) để so sánh cách triển khai
    circle_area = Circle.area
    rectangle_area = Rectangle.area
    triangle_area = Triangle.area
    
    circle_perimeter = Circle.perimeter
    rectangle_perimeter = Rectangle.perimeter
    triangle_perimeter = Triangle.perimeter
    
    # Kiểm tra xem mỗi cách triển khai có khác nhau không
    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 iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Python online