Skip to content

Commit 2084467

Browse files
Wael JabeurWael Jabeur
authored andcommitted
add factory pattern to python patterns plus associated tests
1 parent 9513cfe commit 2084467

File tree

8 files changed

+174
-0
lines changed

8 files changed

+174
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .factory import Factory
2+
from .product_type import ProductType
3+
4+
__all__ = ["Factory", "ProductType"]
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from .product import Product, ConcreteProductA, ConcreteProductB
2+
from .product_type import ProductType
3+
4+
5+
class Factory:
6+
"""
7+
Factory: Provides a method to create objects without
8+
specifying the exact class.
9+
"""
10+
11+
@staticmethod
12+
def create_product(product_type: ProductType) -> Product:
13+
"""
14+
Factory Method: Returns an instance of a
15+
product based on the provided type.
16+
"""
17+
if product_type == ProductType.PRODUCT_A:
18+
return ConcreteProductA()
19+
elif product_type == ProductType.PRODUCT_B:
20+
return ConcreteProductB()
21+
else:
22+
raise ValueError(f"Unknown product type: {product_type}")
23+
24+
# Example Usage:
25+
#
26+
# from src.creational.factory.factory import Factory
27+
# from src.creational.factory.product_type import ProductType
28+
#
29+
# product_a = Factory.create_product(ProductType.PRODUCT_A)
30+
# print(product_a.operation()) # Output: "ConcreteProductA operation executed"
31+
#
32+
# product_b = Factory.create_product(ProductType.PRODUCT_B)
33+
# print(product_b.operation()) # Output: "ConcreteProductB operation executed"
34+
#
35+
# Common Mistakes to Avoid:
36+
#
37+
# 1. Hardcoding Class Names in Factory:
38+
# - Mistake: Using raw strings to determine product types.
39+
# - Fix: Use enums (`ProductType`) to avoid typos and
40+
# improve maintainability.
41+
#
42+
# 2. Allowing Factory to Grow Uncontrollably:
43+
# - Mistake: Adding too many `if-elif` conditions inside the factory.
44+
# - Fix: Use a registry pattern if new products need
45+
# to be added dynamically.
46+
#
47+
# 3. Misusing the Factory for Simple Object Creation:
48+
# - Mistake: Using the factory when simple instantiation
49+
# (`ConcreteProductA()`) is sufficient.
50+
# - Fix: Use Factory only when object creation logic is complex or varies.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from abc import ABC, abstractmethod
2+
3+
4+
class Product(ABC):
5+
"""
6+
Abstract Product: Defines an interface that concrete products must implement.
7+
"""
8+
9+
@abstractmethod
10+
def operation(self) -> str:
11+
"""
12+
Abstract method to be implemented by concrete products.
13+
"""
14+
pass
15+
16+
17+
class ConcreteProductA(Product):
18+
"""
19+
Concrete Product A: Implements the Product interface.
20+
"""
21+
22+
def operation(self) -> str:
23+
return "ConcreteProductA operation executed"
24+
25+
26+
class ConcreteProductB(Product):
27+
"""
28+
Concrete Product B: Implements the Product interface.
29+
"""
30+
31+
def operation(self) -> str:
32+
return "ConcreteProductB operation executed"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# src/creational/factory/product_type.py
2+
3+
from enum import Enum
4+
5+
6+
class ProductType(Enum):
7+
"""
8+
Enum for different product types.
9+
"""
10+
PRODUCT_A = "product_a"
11+
PRODUCT_B = "product_b"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
"""
3+
Tests for the Factory pattern.
4+
"""
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import pytest
2+
from src.creational.factory.factory import Factory
3+
from src.creational.factory.product import ConcreteProductA, ConcreteProductB, Product
4+
from src.creational.factory.product_type import ProductType
5+
6+
7+
def test_factory_create_product_a():
8+
"""
9+
Test that Factory correctly creates ConcreteProductA.
10+
"""
11+
product = Factory.create_product(ProductType.PRODUCT_A)
12+
assert isinstance(product, ConcreteProductA)
13+
# Ensures it follows the Product interface
14+
assert isinstance(product, Product)
15+
assert product.operation() == "ConcreteProductA operation executed"
16+
17+
18+
def test_factory_create_product_b():
19+
"""
20+
Test that Factory correctly creates ConcreteProductB.
21+
"""
22+
product = Factory.create_product(ProductType.PRODUCT_B)
23+
assert isinstance(product, ConcreteProductB)
24+
# Ensures it follows the Product interface
25+
assert isinstance(product, Product)
26+
assert product.operation() == "ConcreteProductB operation executed"
27+
28+
29+
def test_factory_invalid_product():
30+
"""
31+
Test that Factory raises an error when given an invalid ProductType.
32+
"""
33+
with pytest.raises(ValueError, match="Unknown product type"):
34+
# Should raise ValueError
35+
Factory.create_product("invalid_product")
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import pytest
2+
from src.creational.factory.product import ConcreteProductA, ConcreteProductB, Product
3+
4+
5+
def test_concrete_product_a():
6+
"""
7+
Test that ConcreteProductA executes the correct operation.
8+
"""
9+
product = ConcreteProductA()
10+
# Ensure it implements the abstract Product class
11+
assert isinstance(product, Product)
12+
assert product.operation() == "ConcreteProductA operation executed"
13+
14+
15+
def test_concrete_product_b():
16+
"""
17+
Test that ConcreteProductB executes the correct operation.
18+
"""
19+
product = ConcreteProductB()
20+
# Ensure it implements the abstract Product class
21+
assert isinstance(product, Product)
22+
assert product.operation() == "ConcreteProductB operation executed"
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from src.creational.factory.product_type import ProductType
2+
3+
4+
def test_enum_values():
5+
"""
6+
Test that the ProductType enum contains the correct values.
7+
"""
8+
assert ProductType.PRODUCT_A.value == "product_a"
9+
assert ProductType.PRODUCT_B.value == "product_b"
10+
11+
12+
def test_enum_members():
13+
"""
14+
Test that the ProductType enum contains the correct members.
15+
"""
16+
assert list(ProductType) == [ProductType.PRODUCT_A, ProductType.PRODUCT_B]

0 commit comments

Comments
 (0)