I'll show you another popular design pattern: the Factory Pattern. This is widely used in Python's standard library, particularly in the datetime
module.
from abc import ABC, abstractmethod
from datetime import datetime, date
# Abstract Product
class Document(ABC):
@abstractmethod
def create_content(self):
pass
# Concrete Products
class PDFDocument(Document):
def __init__(self, content):
self.content = content
def create_content(self):
return f"PDF Content: {self.content}"
class WordDocument(Document):
def __init__(self, content):
self.content = content
def create_content(self):
return f"Word Content: {self.content}"
class SpreadsheetDocument(Document):
def __init__(self, content):
self.content = content
def create_content(self):
return f"Spreadsheet Content: {self.content}"
# Simple Factory
class SimpleDocumentFactory:
@staticmethod
def create_document(doc_type: str, content: str) -> Document:
if doc_type == "pdf":
return PDFDocument(content)
elif doc_type == "word":
return WordDocument(content)
elif doc_type == "spreadsheet":
return SpreadsheetDocument(content)
else:
raise ValueError(f"Invalid document type: {doc_type}")
# Abstract Factory
class DocumentFactory(ABC):
@abstractmethod
def create_document(self, content: str) -> Document:
pass
@abstractmethod
def create_metadata(self) -> dict:
pass
# Concrete Factories
class PDFFactory(DocumentFactory):
def create_document(self, content: str) -> PDFDocument:
return PDFDocument(content)
def create_metadata(self) -> dict:
return {
"creator": "PDF Factory",
"created_at": datetime.now(),
"file_extension": ".pdf"
}
class WordFactory(DocumentFactory):
def create_document(self, content: str) -> WordDocument:
return WordDocument(content)
def create_metadata(self) -> dict:
return {
"creator": "Word Factory",
"created_at": datetime.now(),
"file_extension": ".docx"
}
# Factory Method
class DocumentCreator(ABC):
@abstractmethod
def factory_method(self) -> Document:
pass
def create_and_log(self, content: str) -> Document:
document = self.factory_method()
document.content = content
print(f"Created document: {document.create_content()}")
return document
class PDFCreator(DocumentCreator):
def factory_method(self) -> PDFDocument:
return PDFDocument("")
class WordCreator(DocumentCreator):
def factory_method(self) -> WordDocument:
return WordDocument("")
# Example Usage
def main():
# Using Simple Factory
print("=== Simple Factory ===")
simple_factory = SimpleDocumentFactory()
pdf = simple_factory.create_document("pdf", "Simple PDF Content")
word = simple_factory.create_document("word", "Simple Word Content")
print(pdf.create_content())
print(word.create_content())
# Using Abstract Factory
print("\\\\n=== Abstract Factory ===")
pdf_factory = PDFFactory()
word_factory = WordFactory()
pdf_doc = pdf_factory.create_document("Complex PDF Content")
pdf_metadata = pdf_factory.create_metadata()
print(pdf_doc.create_content())
print(f"Metadata: {pdf_metadata}")
# Using Factory Method
print("\\\\n=== Factory Method ===")
pdf_creator = PDFCreator()
word_creator = WordCreator()
pdf_doc = pdf_creator.create_and_log("Factory Method PDF")
word_doc = word_creator.create_and_log("Factory Method Word")
if __name__ == "__main__":
main()
Let me explain the three main variations of the Factory Pattern shown here:
json.loads()
which creates different Python objects based on JSON inputdatetime.date.today()
is a factory method that creates a date objectReal-world examples from Python's standard library:
# datetime module using factory method
today = date.today() # Creates a date object for today
# json module using simple factory concept
data = json.loads('{"key": "value"}') # Creates appropriate Python objects
# pathlib using factory method
path = Path('file.txt') # Creates platform-specific path object
Would you like me to:
Point 2
I'll demonstrate how to use the Factory Pattern in a real-world game character creation system, which is a common use case in game development.
from abc import ABC, abstractmethod
from typing import Dict, List
import random
# Base Character Classes
class Character(ABC):
def __init__(self, name: str):
self.name = name
self.health = 100
self.level = 1
self.abilities: List[str] = []
@abstractmethod
def special_attack(self) -> str:
pass
def level_up(self):
self.level += 1
self.health += 20
# Concrete Character Classes
class Warrior(Character):
def __init__(self, name: str):
super().__init__(name)
self.health = 120
self.armor = 10
self.abilities = ["Slash", "Shield Block", "Battle Cry"]
def special_attack(self) -> str:
return f"{self.name} uses Whirlwind Attack!"
class Mage(Character):
def __init__(self, name: str):
super().__init__(name)
self.health = 80
self.mana = 100
self.abilities = ["Fireball", "Frost Nova", "Teleport"]
def special_attack(self) -> str:
return f"{self.name} casts Arcane Explosion!"
class Archer(Character):
def __init__(self, name: str):
super().__init__(name)
self.health = 90
self.arrows = 30
self.abilities = ["Quick Shot", "Multi-Arrow", "Trap"]
def special_attack(self) -> str:
return f"{self.name} uses Rain of Arrows!"
# Equipment Classes
class Equipment:
def __init__(self, name: str, bonus: int):
self.name = name
self.bonus = bonus
class Weapon(Equipment):
pass
class Armor(Equipment):
pass
# Character Factory
class CharacterFactory:
@staticmethod
def create_character(char_type: str, name: str) -> Character:
"""Creates a character with basic equipment"""
if char_type.lower() == "warrior":
character = Warrior(name)
character.weapon = Weapon("Steel Sword", 5)
character.armor = Armor("Chain Mail", 8)
elif char_type.lower() == "mage":
character = Mage(name)
character.weapon = Weapon("Magic Staff", 8)
character.armor = Armor("Cloth Robe", 3)
elif char_type.lower() == "archer":
character = Archer(name)
character.weapon = Weapon("Longbow", 6)
character.armor = Armor("Leather Armor", 5)
else:
raise ValueError(f"Invalid character type: {char_type}")
return character
# Character Customization Factory
class CustomCharacterFactory:
def __init__(self):
self.available_weapons: Dict[str, List[Weapon]] = {
"warrior": [
Weapon("Steel Sword", 5),
Weapon("Battle Axe", 7),
Weapon("War Hammer", 6)
],
"mage": [
Weapon("Magic Staff", 8),
Weapon("Wand", 6),
Weapon("Spell Tome", 7)
],
"archer": [
Weapon("Longbow", 6),
Weapon("Crossbow", 8),
Weapon("Dual Daggers", 7)
]
}
def create_custom_character(self, char_type: str, name: str,
weapon_preference: str = None) -> Character:
"""Creates a character with custom equipment"""
character = CharacterFactory.create_character(char_type, name)
# Assign preferred or random weapon
available_weapons = self.available_weapons[char_type.lower()]
if weapon_preference:
for weapon in available_weapons:
if weapon.name.lower() == weapon_preference.lower():
character.weapon = weapon
break
else:
character.weapon = random.choice(available_weapons)
return character
# Usage Example
def main():
# Basic character creation
factory = CharacterFactory()
warrior = factory.create_character("warrior", "Aragorn")
mage = factory.create_character("mage", "Gandalf")
print(f"Created {warrior.name} (Level {warrior.level})")
print(f"Weapon: {warrior.weapon.name} (+{warrior.weapon.bonus} attack)")
print(f"Abilities: {', '.join(warrior.abilities)}")
print(warrior.special_attack())
print()
# Custom character creation
custom_factory = CustomCharacterFactory()
custom_archer = custom_factory.create_custom_character(
"archer",
"Legolas",
"Longbow"
)
print(f"Created {custom_archer.name} (Level {custom_archer.level})")
print(f"Weapon: {custom_archer.weapon.name} (+{custom_archer.weapon.bonus} attack)")
print(f"Abilities: {', '.join(custom_archer.abilities)}")
print(custom_archer.special_attack())
if __name__ == "__main__":
main()