Claude Artifact

Claude Artifact

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:

  1. Simple Factory
  2. Factory Method
  3. Abstract Factory

Real-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:

  1. Show how to test these factory patterns?
  2. Demonstrate how to use them with a different real-world example?
  3. Explain how Python's built-in libraries implement these patterns in more detail?

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()