Skip to content
This repository has been archived by the owner on Apr 21, 2024. It is now read-only.

Commit

Permalink
Merge pull request #2 from MGTheTrain/feature/design-patterns
Browse files Browse the repository at this point in the history
[Feature] - Design pattern samples in Python
  • Loading branch information
MGTheTrain committed Apr 21, 2024
2 parents 63dc399 + 1de7103 commit 76abcfb
Show file tree
Hide file tree
Showing 12 changed files with 405 additions and 2 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.2.0] - 21-04-2024

### Added

- [Feature] - Coding design patterns for design principles (e.g. SOLID) and their use cases in Python

## [0.1.0] - 21-04-2024

### Added
Expand Down
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,13 @@ build:
.PHONY: clean
clean:
@rm -rf $(BUILD_DIR)

# Target: run-executable
.PHONY: run-executable
run-executable:
@./build/$(arg1)/$(arg2)/$(arg2)

# Target: run-python-script
.PHONY: run-python-script
run-python-script:
@python3 python/$(arg1)/$(arg2)/$(arg2).py
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,23 @@ To compile the C++ source files into executables, execute the following command:
make build
```

In order to remove build artifacts:
To run C++ applications, execute:

```sh
make run-executable arg1=<e.g. design-patterns> arg2=<e.g. adapter, decorator, builder, dependency-injection, observer, singleton, strategy>
# Example: Decorator sample app
make run-executable arg1=design-patterns arg2=decorator
```

To run Python scripts, execute:

```sh
make run-python-script arg1=<e.g. design-patterns> arg2=<e.g. adapter, decorator, builder, dependency-injection, observer, singleton, strategy>
# Example: Builder sample app
make run-python-script arg1=design-patterns arg2=builder
```

In order to remove build artifacts from C++ compilation:

```sh
make clean
Expand Down
Empty file removed python/design-patterns/.gitignore
Empty file.
33 changes: 33 additions & 0 deletions python/design-patterns/adapter/adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from abc import ABC, abstractmethod

class Target(ABC):
"""Interface for the client to interact with."""
@abstractmethod
def request(self):
"""Abstract method to be implemented by concrete classes."""
pass

class Adaptee:
"""Contains some useful behavior with an incompatible interface."""
def specific_request(self):
"""Method representing the specific request of the Adaptee."""
print("Adaptee's specific request")

class Adapter(Target):
"""Adapts the interface of Adaptee to the Target interface."""
def __init__(self, adaptee: Adaptee):
"""Initialize the Adapter with an instance of Adaptee."""
self.adaptee = adaptee

def request(self):
"""Implement the request method to bridge between Target and Adaptee."""
self.adaptee.specific_request()

def main():
"""Entry point of the program."""
adaptee = Adaptee()
adapter = Adapter(adaptee)
adapter.request()

if __name__ == "__main__":
main()
82 changes: 82 additions & 0 deletions python/design-patterns/builder/builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import threading

class Product:
"""Represents the complex object being constructed."""
def __init__(self):
self.parts = []
self.mutex = threading.Lock()

def add_part(self, part):
"""Method to add a part to the product."""
with self.mutex:
self.parts.append(part)

def show_parts(self):
"""Method to display the parts of the product."""
with self.mutex:
print("Product parts:", ", ".join(self.parts))

class Builder:
"""Interface for creating parts of a complex object."""
def build_part_a(self):
"""Method to build part A."""
raise NotImplementedError

def build_part_b(self):
"""Method to build part B."""
raise NotImplementedError

def get_product(self):
"""Method to retrieve the constructed product."""
raise NotImplementedError

class ConcreteBuilder(Builder):
"""Implements the Builder interface to construct a specific product."""
def __init__(self):
self.product = Product()

def build_part_a(self):
"""Method to build part A."""
self.product.add_part("Part A")

def build_part_b(self):
"""Method to build part B."""
self.product.add_part("Part B")

def get_product(self):
"""Method to retrieve the constructed product."""
return self.product

class Director:
"""Responsible for using the builder to construct a complex object."""
def __init__(self, builder: Builder):
self.builder = builder

def construct(self):
"""Method to construct the product using the assigned builder."""
self.builder.build_part_a()
self.builder.build_part_b()

def main():
"""Entry point of the program."""
builder = ConcreteBuilder()
director = Director(builder)

threads = []

def thread_func():
"""Function to be executed by each thread."""
director.construct()
product = builder.get_product()
product.show_parts()

for _ in range(5):
t = threading.Thread(target=thread_func)
threads.append(t)
t.start()

for thread in threads:
thread.join()

if __name__ == "__main__":
main()
62 changes: 62 additions & 0 deletions python/design-patterns/decorator/decorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from abc import ABC, abstractmethod

class Component(ABC):
@abstractmethod
def operation(self):
"""Abstract method representing the operation."""
pass

class ConcreteComponent(Component):
def operation(self):
"""Concrete implementation of the operation."""
print("ConcreteComponent operation")

class Decorator(Component):
def __init__(self, component: Component):
"""
Constructor for the Decorator class.
Args:
component (Component): The component to be decorated.
"""
self.component = component

def operation(self):
"""Delegates the operation to the wrapped component."""
self.component.operation()

class ConcreteDecorator(Decorator):
def operation(self):
"""Overrides the operation to add additional behavior."""
super().operation() # Calls the wrapped component's operation
print("Added behavior in ConcreteDecorator")

class AnotherConcreteDecorator(Decorator):
def operation(self):
"""Overrides the operation to add different behavior."""
super().operation() # Calls the wrapped component's operation
print("Added behavior in AnotherConcreteDecorator")

class AnotherConcreteDecorator2(Decorator):
def operation(self):
"""Overrides the operation to add another behavior."""
super().operation() # Calls the wrapped component's operation
print("Added behavior in AnotherConcreteDecorator2")

def main():
component = ConcreteComponent()
decorated = ConcreteDecorator(component)
decorated.operation()

print("\n")

another_decorated = AnotherConcreteDecorator(decorated)
another_decorated.operation()

print("\n")

another_decorated2 = AnotherConcreteDecorator2(another_decorated)
another_decorated2.operation()

if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from abc import ABC, abstractmethod

class Service(ABC):
@abstractmethod
def execute(self):
"""Abstract method representing the execution of a service."""
pass

class Client:
def __init__(self, service):
"""
Constructor for the Client class.
Args:
service (Service): The service to be used by the client.
"""
self.service = service

def do_something(self):
"""Performs an action using the provided service."""
self.service.execute()

class ConcreteService(Service):
def execute(self):
"""Concrete implementation of the service execution."""
print("Executing ConcreteService")

def main():
# Create a concrete service
service = ConcreteService()
# Create a client with the concrete service
client = Client(service)
# Perform an action using the service
client.do_something()

if __name__ == "__main__":
main()
58 changes: 58 additions & 0 deletions python/design-patterns/observer/observer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from abc import ABC, abstractmethod

class Observer(ABC):
@abstractmethod
def update(self, message):
"""Method called by the Subject when its state changes."""
pass

class Subject:
def __init__(self):
self.observers = []

def attach(self, observer):
"""Attaches an observer to the subject."""
self.observers.append(observer)

def detach(self, observer):
"""Detaches an observer from the subject."""
self.observers.remove(observer)

def notify(self, message):
"""Notifies all observers when the subject's state changes."""
for observer in self.observers:
observer.update(message)

class ConcreteObserver1(Observer):
def update(self, message):
"""Prints a message upon receiving an update from the Subject."""
print("ConcreteObserver1 received message:", message)

class ConcreteObserver2(Observer):
def update(self, message):
"""Prints a message upon receiving an update from the Subject."""
print("ConcreteObserver2 received message:", message)

def main():
# Create a subject
subject = Subject()

# Create observers
observer1 = ConcreteObserver1()
observer2 = ConcreteObserver2()

# Attach observers to the subject
subject.attach(observer1)
subject.attach(observer2)

# Notify observers
subject.notify("Hello Observers!")

# Detach observer2
subject.detach(observer2)

# Notify observers again
subject.notify("Observers, are you there?")

if __name__ == "__main__":
main()
43 changes: 43 additions & 0 deletions python/design-patterns/singleton/singleton.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import threading

class Singleton:
_instance = None # Private static variable to hold the singleton instance
_lock = threading.Lock() # Lock for thread safety

@staticmethod
def getInstance():
"""
Static method to retrieve the singleton instance.
Returns:
Singleton: The singleton instance.
"""
if Singleton._instance is None:
with Singleton._lock: # Acquire the lock for thread safety
if Singleton._instance is None: # Double check to ensure thread safety
print("Singleton instance created")
Singleton._instance = Singleton()
return Singleton._instance

def printMessage(self):
"""Method to print a test message."""
print("Test...")

def threadFunc():
"""Function to be executed by each thread."""
singleton = Singleton.getInstance()
singleton.printMessage()

def main():
threads = []
for _ in range(5):
threads.append(threading.Thread(target=threadFunc))

for thread in threads:
thread.start()

for thread in threads:
thread.join()

if __name__ == "__main__":
main()
Loading

0 comments on commit 76abcfb

Please sign in to comment.