Skip to content

Add five new pattern examples with demos#1

Open
bhrionn wants to merge 1 commit intomainfrom
claude/add-example-patterns-018so3i3kk473AQu33MzkJwR
Open

Add five new pattern examples with demos#1
bhrionn wants to merge 1 commit intomainfrom
claude/add-example-patterns-018so3i3kk473AQu33MzkJwR

Conversation

@bhrionn
Copy link
Copy Markdown
Owner

@bhrionn bhrionn commented Nov 17, 2025

This commit adds comprehensive implementations and documentation for five design patterns:

Creational Patterns:

  • Prototype: Game character cloning system with deep/shallow copy examples

Structural Patterns:

  • Facade: Home theater system with simplified interface
  • Proxy: Virtual, protection, and cache proxy implementations

Behavioral Patterns:

  • State: Media player with state transitions (Stopped, Playing, Paused)
  • Template Method: Data processing pipeline for CSV/JSON/XML

Each pattern includes:

  • theory.md: Comprehensive pattern documentation with benefits, use cases, Python-specific considerations, and best practices
  • example.py: Production-quality code with SOLID principles, type hints, comprehensive documentation, and multiple demonstrations

Updated tasks.md to reflect completion of these patterns.

This commit adds comprehensive implementations and documentation for five
design patterns:

Creational Patterns:
- Prototype: Game character cloning system with deep/shallow copy examples

Structural Patterns:
- Facade: Home theater system with simplified interface
- Proxy: Virtual, protection, and cache proxy implementations

Behavioral Patterns:
- State: Media player with state transitions (Stopped, Playing, Paused)
- Template Method: Data processing pipeline for CSV/JSON/XML

Each pattern includes:
- theory.md: Comprehensive pattern documentation with benefits, use cases,
  Python-specific considerations, and best practices
- example.py: Production-quality code with SOLID principles, type hints,
  comprehensive documentation, and multiple demonstrations

Updated tasks.md to reflect completion of these patterns.
Copilot AI review requested due to automatic review settings November 17, 2025 21:02
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @bhrionn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly expands the project's design pattern examples by introducing five new, thoroughly documented implementations: Prototype, Facade, Proxy, State, and Template Method. The goal is to provide clear, practical, and well-structured code examples that illustrate the application of these patterns, adhering to SOLID principles and Python best practices. This enhancement serves as a valuable resource for understanding and applying these fundamental software design concepts.

Highlights

  • New Design Pattern Examples: Five new design pattern implementations have been added: Prototype (Creational), Facade (Structural), Proxy (Structural), State (Behavioral), and Template Method (Behavioral).
  • Comprehensive Documentation: Each new pattern includes a theory.md file providing in-depth explanations, benefits, use cases, and Python-specific considerations.
  • Production-Quality Code: Each pattern is accompanied by an example.py file featuring production-quality code, type hints, comprehensive documentation, and multiple demonstrations.
  • Task Tracking Update: The tasks.md file has been updated to reflect the completion of these five design patterns.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This is a great contribution that adds five well-documented design pattern examples. The code is clear, follows good practices, and the accompanying theory files are comprehensive. The demonstrations for each pattern are particularly helpful for understanding their practical application and benefits. I've provided a few suggestions to further improve the code, mainly around strengthening adherence to design principles like Dependency Inversion and improving robustness.

"""Enumeration of power states for devices."""
ON = "on"
OFF = "off"
STANDBY = "standby"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The STANDBY member of the PowerState enum is defined but never used throughout the example. To avoid dead code, you could either remove it or add logic that utilizes the standby state.

Comment on lines +315 to +323
def __init__(
self,
dvd_player: DVDPlayer,
projector: Projector,
screen: Screen,
amplifier: Amplifier,
lights: Lights,
streaming_device: StreamingDevice
):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The docstrings for the file (line 21) and the HomeTheaterFacade class claim to demonstrate the Dependency Inversion Principle. However, the __init__ method depends on concrete subsystem classes (DVDPlayer, Projector, etc.) rather than abstractions. To better adhere to DIP, you could define abstract base classes or protocols for the subsystem components and have the facade depend on those. This would make the facade more flexible and testable, as you could pass in mock or alternative implementations.

Comment on lines +31 to +33
projector.set_input(dvd_player)
amplifier.on()
amplifier.set_dvd(dvd_player)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The code example in the 'Problem' section is slightly inconsistent with the example.py implementation. Here, projector.set_input() and amplifier.set_dvd() are shown taking the dvd_player object as an argument. In example.py, set_input methods take a string (e.g., 'DVD'), and the Amplifier class does not have a set_dvd method. Aligning this example with the implementation would improve clarity.

Comment on lines +164 to +172
@abstractmethod
def clone(self) -> 'GameCharacter':
"""
Clone this character (deep copy).

Returns:
A new instance that is a deep copy of this character
"""
pass
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The clone method is marked as abstract here, forcing each subclass (Warrior, Mage, Archer) to implement it. However, all implementations are identical (return copy.deepcopy(self)). You could make this a concrete method in the GameCharacter base class to avoid code duplication by removing the @abstractmethod decorator and providing the implementation here. Subclasses can still override it if they need custom cloning logic, and type checkers will handle the covariant return types correctly.

Suggested change
@abstractmethod
def clone(self) -> 'GameCharacter':
"""
Clone this character (deep copy).
Returns:
A new instance that is a deep copy of this character
"""
pass
def clone(self) -> 'GameCharacter':
"""
Clone this character (deep copy).
This is a concrete implementation that can be inherited by subclasses.
Returns:
A new instance that is a deep copy of this character
"""
return copy.deepcopy(self)

print(f"Average time per creation: {from_scratch_time/10:.4f} seconds")
print()

speedup = from_scratch_time / cloning_time
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's a potential for a ZeroDivisionError here if cloning_time is zero. While unlikely in this example due to time.sleep(), it's good practice to guard against this, especially in performance-critical code where the duration could be negligible.

Suggested change
speedup = from_scratch_time / cloning_time
speedup = from_scratch_time / cloning_time if cloning_time > 0 else float('inf')

True if access granted, False otherwise
"""
# Write requires at least one level higher than document classification
required_level = min(self._real_document.classification.value + 1, 4)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The number 4 is used here as a magic number. It corresponds to the value of AccessLevel.TOP_SECRET. Using the enum member AccessLevel.TOP_SECRET.value directly would make the code more readable and maintainable, as it avoids relying on the hardcoded integer value of the enum.

Suggested change
required_level = min(self._real_document.classification.value + 1, 4)
required_level = min(self._real_document.classification.value + 1, AccessLevel.TOP_SECRET.value)

- Performance optimization
"""

def __init__(self, real_database: RealDatabase):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To better adhere to the Dependency Inversion Principle, the __init__ method of CachingDatabaseProxy should depend on the DatabaseQuery protocol abstraction rather than the concrete RealDatabase class. This would make the proxy more flexible and allow it to work with any object that conforms to the DatabaseQuery protocol.

Suggested change
def __init__(self, real_database: RealDatabase):
def __init__(self, real_database: DatabaseQuery):

proxy_time = time.time() - start
print(f"Total time: {proxy_time:.3f}s\n")

print(f"Speedup: {load_time/proxy_time:.2f}x faster with proxy\n")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's a potential for a ZeroDivisionError here if proxy_time is zero. While unlikely in this specific demonstration due to the time.sleep() call, it's a good defensive programming practice to handle this edge case. You could add a check for proxy_time > 0 before performing the division.


self._playlist = playlist
self._current_track_index = 0
self._state = StoppedState()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The initial state is set directly here, but its on_enter method is not called. This is inconsistent with how subsequent state transitions are handled by set_state, which does call on_enter. This leads to the initial 'Entering StoppedState' message not being printed. To ensure consistent behavior, you should call self._state.on_enter(self) right after this line.

# Simplified parsing for demonstration
# In real implementation, would use xml.etree or lxml
records = []
import re
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The import re statement is located inside the parse_data method. According to PEP 8, imports should be at the top of the file. Moving this import to the top improves code style and avoids re-importing the module on every method call.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds comprehensive implementations and documentation for five design patterns: Prototype (creational), Facade and Proxy (structural), and State and Template Method (behavioral). Each pattern includes detailed theory documentation covering intent, benefits, use cases, Python-specific considerations, and best practices, along with production-quality example code demonstrating SOLID principles with type hints and comprehensive demonstrations.

Key Changes

  • Added complete implementation and documentation for five design patterns with real-world examples
  • Each pattern includes theory.md with comprehensive pattern documentation and example.py with multiple demonstrations
  • Updated tasks.md to mark the five patterns as completed

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 16 comments.

Show a summary per file
File Description
tasks.md Updated task checklist to mark five patterns as completed (Prototype, State, Template Method, Facade, Proxy)
examples/template_method_pattern/theory.md Comprehensive documentation for Template Method pattern covering algorithm structure, hook methods, and Python-specific implementation
examples/template_method_pattern/example.py Data processing pipeline implementation demonstrating CSV/JSON/XML processors with template method structure
examples/state_pattern/theory.md Complete State pattern documentation explaining state transitions, context delegation, and state-specific behavior
examples/state_pattern/example.py Media player implementation with Stopped, Playing, and Paused states demonstrating state transitions
examples/proxy_pattern/theory.md Proxy pattern documentation covering virtual, protection, and cache proxy types with use cases
examples/proxy_pattern/example.py Three proxy implementations: virtual (lazy loading), protection (access control), and cache (result caching)
examples/prototype_pattern/theory.md Prototype pattern documentation explaining cloning, shallow vs deep copy, and prototype registry
examples/prototype_pattern/example.py Game character cloning system demonstrating deep/shallow copy with performance comparisons
examples/facade_pattern/theory.md Facade pattern documentation explaining simplified interfaces and subsystem coordination
examples/facade_pattern/example.py Home theater system facade coordinating multiple components (DVD, projector, amplifier, lights)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- [ ] Abstract Factory - Provides an interface for creating families of related objects without specifying their concrete classes.
- [x] Builder - Separates complex object construction from its representation, allowing step-by-step construction.
- [ ] Prototype - Creates new objects by copying existing instances (cloning).
- [x] Prototype - Creates new objects by copying existing instances (cloning).
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line numbering is inconsistent. Lines 8 and 17 have their numbers included, while line 19 doesn't have a line number. This creates an inconsistent formatting pattern in the tasks list.

Copilot uses AI. Check for mistakes.
- [x] State - Allows an object to alter its behavior when its internal state changes.
- [x] Strategy - Defines a family of algorithms, encapsulates each one, and makes them interchangeable.
- [ ] Template Method - Defines the skeleton of an algorithm in a base class, letting subclasses override specific steps.
- [x] Template Method - Defines the skeleton of an algorithm in a base class, letting subclasses override specific steps.
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line numbering is inconsistent. Lines 8 and 17 have their numbers included, while line 19 doesn't have a line number. This creates an inconsistent formatting pattern in the tasks list.

Copilot uses AI. Check for mistakes.
- [x] Facade - Provides a simplified interface to a complex subsystem.
- [ ] Flyweight - Shares common state between multiple objects to save memory.
- [ ] Proxy - Provides a surrogate or placeholder to control access to another object. No newline at end of file
- [x] Proxy - Provides a surrogate or placeholder to control access to another object. No newline at end of file
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line numbering is inconsistent. Lines 27 and 29 have their numbers included, but the pattern is not uniform throughout the file. This creates an inconsistent formatting pattern in the tasks list.

Copilot uses AI. Check for mistakes.
Comment on lines +232 to +237
# Exit current state (stop audio)
self.on_exit(player)
player.move_to_previous_track()
print(f"[Player] ⏮️ Previous track: {player.current_track}")
# Re-enter state (start new audio)
self.on_enter(player)
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the previous_track method override, on_exit and on_enter are called manually without properly updating the state through the player's set_state method. This bypasses the state transition mechanism and could lead to inconsistent state history tracking. The method should either use set_state properly or avoid calling entry/exit hooks manually.

Copilot uses AI. Check for mistakes.
- [ ] Mediator - Defines an object that encapsulates how objects interact, promoting loose coupling.
- [ ] Memento - Captures and externalizes an object's internal state for later restoration without violating encapsulation.
- [ ] State - Allows an object to alter its behavior when its internal state changes.
- [x] State - Allows an object to alter its behavior when its internal state changes.
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line numbering is inconsistent. Lines 8 and 17 have their numbers included, while line 19 doesn't have a line number. This creates an inconsistent formatting pattern in the tasks list.

Copilot uses AI. Check for mistakes.
# Without proxy - all images loaded immediately
print("Without proxy (loading 5 images immediately):")
start = time.time()
real_images = [RealImage(f"photo{i}.jpg") for i in range(5)]
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable real_images is not used.

Suggested change
real_images = [RealImage(f"photo{i}.jpg") for i in range(5)]
[RealImage(f"photo{i}.jpg") for i in range(5)]

Copilot uses AI. Check for mistakes.
"""

from abc import ABC, abstractmethod
from typing import Optional, List
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'Optional' is not used.

Suggested change
from typing import Optional, List
from typing import List

Copilot uses AI. Check for mistakes.
"""

from abc import ABC, abstractmethod
from typing import List, Dict, Any, Optional
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'Optional' is not used.

Suggested change
from typing import List, Dict, Any, Optional
from typing import List, Dict, Any

Copilot uses AI. Check for mistakes.
- Dependency Inversion: Facade depends on abstractions (component protocols)
"""

from abc import ABC, abstractmethod
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'ABC' is not used.
Import of 'abstractmethod' is not used.

Suggested change
from abc import ABC, abstractmethod

Copilot uses AI. Check for mistakes.
"""

from abc import ABC, abstractmethod
from typing import Optional, List
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'List' is not used.

Suggested change
from typing import Optional, List
from typing import Optional

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants