-You will find a familiarity with Design Patterns very useful when planning, discussing, developing, managing and documenting your applications from now on and into the future.
+
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08XLJ8Z2J
-You will learn these Design Patterns
+## Course Access
-* Creational
- * [Factory](factory)
- * [Abstract Factory](abstract_factory)
- * [Builder](builder)
- * [Prototype](prototype)
- * Singleton
-* Structural
- * [Decorator](decorator)
- * [Adapter](adapter)
- * [Facade](facade)
- * [Bridge](bridge)
- * [Composite](composite)
- * Flyweight
- * [Proxy](proxy)
-* Behavioural
- * [Command](command)
- * [Chain of Responsibility](chain_of_responsibility)
- * [Observer Pattern](observer)
- * Interpreter
- * [Iterator](iterator)
- * [Mediator](mediator)
- * Memento
- * State
- * Strategy
- * Template
- * Visitor
+There are 3 possible ways to access the video content in this course,
+1. Udemy : [https://www.udemy.com/course/design-patterns-in-python/?referralCode=7493DBBBF97FF2B0D24D](https://www.udemy.com/course/design-patterns-in-python/?referralCode=7493DBBBF97FF2B0D24D)
+ - Get **Udemy Discount Coupons** at [https://sbcode.net/coupons](https://sbcode.net/coupons)
+ - Certificate of Completion
+ - 30 Day Money Back Guarantee
+2. YouTube Membership : [https://www.youtube.com/channel/UCmUILI2AWt2MSUgPlZwFdOg/join](https://www.youtube.com/channel/UCmUILI2AWt2MSUgPlZwFdOg/join)
+ - Cancel Membership Anytime
+3. Book : [https://amzn.to/466lBN6](https://amzn.to/466lBN6) : ASIN B08XLJ8Z2J
+ - **Book** includes FREE Video Access Codes to view videos from the official documentation website at [https://sbcode.net/python/](https://sbcode.net/python/)
-Register for the course at
-- [Skillshare](https://skl.sh/34SM2Xg) **(Get Extra 2 Months Premium Membership Free)**
-- [Udemy](https://www.udemy.com/course/design-patterns-in-python/?referralCode=7493DBBBF97FF2B0D24D) **(New Student Discount)**
+All the code examples in the book can be found in these pages.
+---
-## Introduction Video
+**TIP**
-[](https://youtu.be/OOxyTUWsY7A)
+> [Design Patterns In python](https://www.amazon.com/dp/B08XLJ8Z2J) **(Paperback/Kindle)** includes Video Access Codes to view videos for FREE from the official documentation website at [https://sbcode.net/python/](https://sbcode.net/python/)
+---
+**TIP**
-To register for this course visit
+> Get **Udemy Discount Coupons** at [https://sbcode.net/coupons](https://sbcode.net/coupons)
-
+---
-- Get 2 Months Free Premium Membership to 1000s of Courses
-- Full Lifetime Access
-- Subscription Model
-- Cancel Any Time
+## Overview
-
+A Design Pattern is a description or template that can be repeatedly applied to a commonly recurring problem in software design.
-- One Time Payment
-- Full Lifetime Access
-- Certificate of Completion
-- 30 Day Money-Back Guarantee
+A familiarity of Design Patterns will be very useful when planning, discussing, managing and documenting your applications from now on and into the future.
+
+Also, throughout the book, as each design pattern is discussed and demonstrated using example code, I also introduce new python coding concepts with each new design pattern. So that as you progress through the book and try out the examples, you will also get experience and familiarity with some finer details of programming with python.
+
+So, in this book, you will learn about these 23 Design Patterns,
+
+- Creational
+ - [Factory](factory)
+ - [Abstract Factory](abstract_factory)
+ - [Builder](builder)
+ - [Prototype](prototype)
+ - [Singleton](singleton)
+- Structural
+ - [Decorator](decorator)
+ - [Adapter](adapter)
+ - [Facade](facade)
+ - [Bridge](bridge)
+ - [Composite](composite)
+ - [Flyweight](flyweight)
+ - [Proxy](proxy)
+- Behavioral
+ - [Command](command)
+ - [Chain of Responsibility](chain_of_responsibility)
+ - [Observer Pattern](observer)
+ - [Interpreter](interpreter)
+ - [Iterator](iterator)
+ - [Mediator](mediator)
+ - [Memento](memento)
+ - [State](state)
+ - [Strategy](strategy)
+ - [Template](template)
+ - [Visitor](visitor)
+
+## Pattern Types
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Class Scope and Object Scope Patterns
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
diff --git a/abstract_factory/Abstract Factory Design Pattern.md b/abstract_factory/Abstract Factory Design Pattern.md
deleted file mode 100644
index 343c538..0000000
--- a/abstract_factory/Abstract Factory Design Pattern.md
+++ /dev/null
@@ -1,59 +0,0 @@
-# Abstract Factory Design Pattern
-
-The Abstract Factory Pattern adds an abstract layer over multiple factory method implementations.
-
-The Abstract Factory contains or composites one or more than one factory method
-
-
-
-Abstract Factory in the context of a Furniture factory
-
-
-
-Import existing factories
-```python
-from chair_factory import ChairFactory
-from table_factory import TableFactory
-```
-
-Create an interface
-```python
-class IFurnitureFactory(metaclass=ABCMeta):
- """Furniture Factory Interface"""
-
- @abstractstaticmethod
- def get_furniture(furniture):
- """The static funiture factory interface method"""
-```
-
-The factories abstract static method which delegates to the correct factory
-```python
-
-class FurnitureFactory(IFurnitureFactory):
- """The Furniture Factory Concrete Class"""
-
- @staticmethod
- def get_furniture(furniture):
- """Static get_furniture method"""
- try:
- if furniture in ["SmallChair", "MediumChair", "BigChair"]:
- return ChairFactory().get_chair(furniture)
- if furniture in ["SmallTable", "MediumTable", "BigTable"]:
- return TableFactory().get_table(furniture)
- raise AssertionError("No Furniture Factory Found")
- except AssertionError as _e:
- print(_e)
- return None
-```
-
-
-Requesting from the abstract factory at run time
-```python
-if __name__ == "__main__":
- FURNITURE = FurnitureFactory.get_furniture("SmallChair")
- print(f"{FURNITURE.__class__} : {FURNITURE.dimensions()}")
-
- FURNITURE = FurnitureFactory.get_furniture("MediumTable")
- print(f"{FURNITURE.__class__} : {FURNITURE.dimensions()}")
-```
-
diff --git a/abstract_factory/Abstract Factory Design Pattern.pdf b/abstract_factory/Abstract Factory Design Pattern.pdf
deleted file mode 100644
index 163fc79..0000000
Binary files a/abstract_factory/Abstract Factory Design Pattern.pdf and /dev/null differ
diff --git a/abstract_factory/README.md b/abstract_factory/README.md
index f9a4525..fe3f1c5 100644
--- a/abstract_factory/README.md
+++ b/abstract_factory/README.md
@@ -1,58 +1,120 @@
# Abstract Factory Design Pattern
-The Abstract Factory Pattern adds an abstract layer over multiple factory method implementations.
+## Videos
-The Abstract Factory contains or composites one or more than one factory method
+Section | Video Links
+-|-
+Abstract Factory Overview |
+Abstract Factory Use Case |
+Exception Handling |
-
+## Book
-Abstract Factory in the context of a Furniture factory
-
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
+## Overview
-Import existing factories
-```python
-from chair_factory import ChairFactory
-from table_factory import TableFactory
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Abstract Factory UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./abstract_factory/abstract_factory_concept.py
+
+Adapter Use Case |
+Python **isinstance()** Function |
+Python **time** Module |
-In this lecture, I have 2 classes, they don't share the same interface. The client requires it's objects to use an already standardised interface.
+## Book
-So we need to create an adapter, that wraps the incompatible object, but implements the standardised interface.
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
-## Two Incompatible Classes
-
+## Overview
-## After Creating an Adapter
-
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Adapter UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Output
+
+``` bash
+python ./adapter/adapter_concept.py
+method A
+method B
+method A
+method B
+```
+
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./adapter/client.py
+Company A is busy, trying company B
+Company B is busy, trying company A
+Company A is busy, trying company B
+Company B is busy, trying company A
+Company A building Cube id:2968196317136, 2x3x7
+Company A is busy, trying company B
+Company B building Cube id:2968196317136, 8x2x8
+Company A building Cube id:2968196317040, 4x6x4
+Company A is busy, trying company B
+Company B is busy, trying company A
+Company A building Cube id:2968196317136, 5x4x8
+Company A is busy, trying company B
+Company B building Cube id:2968196317136, 2x2x9
+5 cubes have been manufactured
+```
+
+## New Coding Concepts
+
+### Python `isinstance()` Function
+
+Syntax: `isinstance(object, type)`
+
+Returns: `True` or `False`
+
+You can use the inbuilt function `isinstance()` to conditionally check the `type` of an object.
+
+``` python
+>>> isinstance(1,int)
+True
+>>> isinstance(1,bool)
+False
+>>> isinstance(True,bool)
+True
+>>> isinstance("abc",str)
+True
+>>> isinstance("abc",(int,list,dict,tuple,set))
+False
+>>> isinstance("abc",(int,list,dict,tuple,set,str))
+True
+```
+
+You can also test your custom classes.
+
+``` python
+class my_class:
+ "nothing to see here"
+
+CLASS_A = my_class()
+print(type(CLASS_A))
+print(isinstance(CLASS_A, bool))
+print(isinstance(CLASS_A, my_class))
+```
+
+Outputs
+
+```
+
+
|
+| Bridge Use Case |
|
+| Python **Tuple** |
|
+| Python **\*args** |
|
+
+## Book
+
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
+
+## Overview
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Bridge UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Output
+
+``` bash
+python ./bridge/bridge_concept.py
+('a', 'b', 'c')
+a
+b
+c
+```
+
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./bridge/client.py
+ ******
+ ** **
+ * *
+
+* *
+* *
+
+ * *
+ ** **
+ ******
+**************
+
+* *
+* *
+* *
+* *
+* *
+* *
+
+**************
+```
+
+## New Coding Concepts
+
+### The `*args` Argument.
+
+The `*args` argument takes all arguments that were sent to this method, and packs them into a [Tuple](#python-tuple).
+
+It is useful when you don't know how many arguments, or what types, will be sent to a method, and you want the method to support any number of arguments or types being sent to it.
+
+If you want your method to be strict about the types that it can accept, the set it specifically to accept [List](/builder#python-list), [Dictionary](/singleton#python-dictionary), [Set](/observer#python-set) or [Tuple](#python-tuple), and treat the argument as such within the method body, but the `*args` argument is another common option that you will see in source code throughout the internet.
+
+E.g., when using the `*args` in your method signature, you can call it with any number of arguments of any type.
+
+``` python
+def my_method(*args):
+ for arg in args:
+ print(arg)
+
+my_method(1, 22, [3], {4})
+```
+
+Outputs
+
+``` bash
+1
+22
+[3]
+{4}
+```
+
+### Python Tuple
+
+A Python **Tuple** is similar to a [List](/builder#python-list). Except that the items in the Tuple are ordered, unchangeable and allow duplicates.
+
+A Tuple can be instantiated using the round brackets `()` or `tuple()` , verses `[]` for a [List](/builder#python-list) and `{}` for a [Set](/observer#python-set) or [Dictionary](/singleton#python-dictionary).
+
+``` python
+PS> python
+>>> items = ("alpha", "bravo", "charlie", "alpha")
+>>> print(items)
+('alpha', 'bravo', 'charlie', 'alpha')
+>>> print(len(items))
+4
+```
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/bridge/bridge.py b/bridge/bridge.py
deleted file mode 100644
index e6946ef..0000000
--- a/bridge/bridge.py
+++ /dev/null
@@ -1,79 +0,0 @@
-"""
-Bridge pattern example.
-"""
-from abc import ABCMeta, abstractmethod
-
-
-NOT_IMPLEMENTED = "You should implement this."
-
-
-class DrawingAPI:
- __metaclass__ = ABCMeta
-
- @abstractmethod
- def draw_circle(self, x, y, radius):
- raise NotImplementedError(NOT_IMPLEMENTED)
-
-
-class DrawingAPI1(DrawingAPI):
- def draw_circle(self, x, y, radius):
- return "API1.circle at {0}:{1} - radius: {2}".format(x, y, radius)
-
-
-class DrawingAPI2(DrawingAPI):
- def draw_circle(self, x, y, radius):
- return "API2.circle at {0}:{1} - radius: {2}".format(x, y, radius)
-
-class DrawingAPI3(DrawingAPI):
- def draw_circle(self, x, y, radius):
- return "API3.circle at {0}:{1} - radius: {2}".format(x, y, radius)
-
-
-class Shape:
- __metaclass__ = ABCMeta
-
- drawing_api = None
- def __init__(self, drawing_api):
- self.drawing_api = drawing_api
-
- @abstractmethod
- def draw(self):
- raise NotImplementedError(NOT_IMPLEMENTED)
-
- @abstractmethod
- def resize_by_percentage(self, percent):
- raise NotImplementedError(NOT_IMPLEMENTED)
-
-
-class CircleShape(Shape):
- def __init__(self, x, y, radius, drawing_api):
- self.x = x
- self.y = y
- self.radius = radius
- super(CircleShape, self).__init__(drawing_api)
-
-
- def draw(self):
- return self.drawing_api.draw_circle(
- self.x, self.y, self.radius
- )
-
- def resize_by_percentage(self, percent):
- self.radius *= (1 + percent/100)
-
-
-class BridgePattern(object):
- @staticmethod
- def test():
- shapes = [
- CircleShape(1.0, 2.0, 3.0, DrawingAPI1()),
- CircleShape(5.0, 7.0, 11.0, DrawingAPI2()),
- CircleShape(5.0, 4.0, 12.0, DrawingAPI3())
- ]
-
- for shape in shapes:
- shape.resize_by_percentage(2.5)
- print(shape.draw())
-
-
-BridgePattern.test()
\ No newline at end of file
diff --git a/bridge/bridge_concept.py b/bridge/bridge_concept.py
new file mode 100644
index 0000000..3ba44b8
--- /dev/null
+++ b/bridge/bridge_concept.py
@@ -0,0 +1,60 @@
+# pylint: disable=too-few-public-methods
+# pylint: disable=arguments-differ
+"Bridge Pattern Concept Sample Code"
+from abc import ABCMeta, abstractmethod
+
+class IAbstraction(metaclass=ABCMeta):
+ "The Abstraction Interface"
+
+ @staticmethod
+ @abstractmethod
+ def method(*args):
+ "The method handle"
+
+class RefinedAbstractionA(IAbstraction):
+ "A Refined Abstraction"
+
+ def __init__(self, implementer):
+ self.implementer = implementer()
+
+ def method(self, *args):
+ self.implementer.method(*args)
+
+class RefinedAbstractionB(IAbstraction):
+ "A Refined Abstraction"
+
+ def __init__(self, implementer):
+ self.implementer = implementer()
+
+ def method(self, *args):
+ self.implementer.method(*args)
+
+class IImplementer(metaclass=ABCMeta):
+ "The Implementer Interface"
+
+ @staticmethod
+ @abstractmethod
+ def method(*args: tuple) -> None:
+ "The method implementation"
+
+class ConcreteImplementerA(IImplementer):
+ "A Concrete Implementer"
+
+ @staticmethod
+ def method(*args: tuple) -> None:
+ print(args)
+
+class ConcreteImplementerB(IImplementer):
+ "A Concrete Implementer"
+
+ @staticmethod
+ def method(*args: tuple) -> None:
+ for arg in args:
+ print(arg)
+
+# The Client
+REFINED_ABSTRACTION_A = RefinedAbstractionA(ConcreteImplementerA)
+REFINED_ABSTRACTION_A.method('a', 'b', 'c')
+
+REFINED_ABSTRACTION_B = RefinedAbstractionB(ConcreteImplementerB)
+REFINED_ABSTRACTION_B.method('a', 'b', 'c')
diff --git a/bridge/circle.py b/bridge/circle.py
new file mode 100644
index 0000000..a87d2cc
--- /dev/null
+++ b/bridge/circle.py
@@ -0,0 +1,13 @@
+# pylint: disable=too-few-public-methods
+"A Circle Abstraction"
+from interface_shape import IShape
+
+
+class Circle(IShape):
+ "The Circle is a Refined Abstraction"
+
+ def __init__(self, implementer):
+ self.implementer = implementer()
+
+ def draw(self):
+ self.implementer.draw_implementation()
diff --git a/bridge/circle_implementer.py b/bridge/circle_implementer.py
new file mode 100644
index 0000000..7f8b4fa
--- /dev/null
+++ b/bridge/circle_implementer.py
@@ -0,0 +1,17 @@
+# pylint: disable=too-few-public-methods
+"A Circle Implementer"
+from interface_shape_implementer import IShapeImplementer
+
+
+class CircleImplementer(IShapeImplementer):
+ "A Circle Implementer"
+
+ def draw_implementation(self):
+ print(" ******")
+ print(" ** **")
+ print(" * *")
+ print("* *")
+ print("* *")
+ print(" * *")
+ print(" ** **")
+ print(" ******")
diff --git a/bridge/classes.dot b/bridge/classes.dot
deleted file mode 100644
index 9c9847a..0000000
--- a/bridge/classes.dot
+++ /dev/null
@@ -1,15 +0,0 @@
-digraph "classes" {
-charset="utf-8"
-rankdir=BT
-"0" [label="{BridgePattern|\l|test()\l}", shape="record"];
-"1" [label="{CircleShape|radius\lx\ly\l|draw()\lresize_by_percentage()\l}", shape="record"];
-"2" [label="{DrawingAPI|\l|draw_circle()\l}", shape="record"];
-"3" [label="{DrawingAPI1|\l|draw_circle()\l}", shape="record"];
-"4" [label="{DrawingAPI2|\l|draw_circle()\l}", shape="record"];
-"5" [label="{DrawingAPI3|\l|draw_circle()\l}", shape="record"];
-"6" [label="{Shape|drawing_api\ldrawing_api : NoneType\l|draw()\lresize_by_percentage()\l}", shape="record"];
-"1" -> "6" [arrowhead="empty", arrowtail="none"];
-"3" -> "2" [arrowhead="empty", arrowtail="none"];
-"4" -> "2" [arrowhead="empty", arrowtail="none"];
-"5" -> "2" [arrowhead="empty", arrowtail="none"];
-}
diff --git a/bridge/client.py b/bridge/client.py
new file mode 100644
index 0000000..1673346
--- /dev/null
+++ b/bridge/client.py
@@ -0,0 +1,12 @@
+"Bridge Pattern Concept Sample Code"
+
+from circle_implementer import CircleImplementer
+from square_implementer import SquareImplementer
+from circle import Circle
+from square import Square
+
+CIRCLE = Circle(CircleImplementer)
+CIRCLE.draw()
+
+SQUARE = Square(SquareImplementer)
+SQUARE.draw()
diff --git a/bridge/interface_shape.py b/bridge/interface_shape.py
new file mode 100644
index 0000000..17df0cf
--- /dev/null
+++ b/bridge/interface_shape.py
@@ -0,0 +1,12 @@
+# pylint: disable=too-few-public-methods
+"The Shape Abstraction Interface"
+from abc import ABCMeta, abstractmethod
+
+
+class IShape(metaclass=ABCMeta):
+ "The Shape Abstraction Interface"
+
+ @staticmethod
+ @abstractmethod
+ def draw():
+ "The method that will be handled at the shapes implementer"
diff --git a/bridge/interface_shape_implementer.py b/bridge/interface_shape_implementer.py
new file mode 100644
index 0000000..80b36f8
--- /dev/null
+++ b/bridge/interface_shape_implementer.py
@@ -0,0 +1,12 @@
+# pylint: disable=too-few-public-methods
+"A Shape Implementor Interface"
+from abc import ABCMeta, abstractmethod
+
+
+class IShapeImplementer(metaclass=ABCMeta):
+ "Shape Implementer"
+
+ @staticmethod
+ @abstractmethod
+ def draw_implementation():
+ "The method that the refined abstractions will implement"
diff --git a/bridge/square.py b/bridge/square.py
new file mode 100644
index 0000000..9f174cb
--- /dev/null
+++ b/bridge/square.py
@@ -0,0 +1,13 @@
+# pylint: disable=too-few-public-methods
+"A Square Abstraction"
+from interface_shape import IShape
+
+
+class Square(IShape):
+ "The Square is a Refined Abstraction"
+
+ def __init__(self, implementer):
+ self.implementer = implementer()
+
+ def draw(self):
+ self.implementer.draw_implementation()
diff --git a/bridge/square_implementer.py b/bridge/square_implementer.py
new file mode 100644
index 0000000..efac3b1
--- /dev/null
+++ b/bridge/square_implementer.py
@@ -0,0 +1,17 @@
+# pylint: disable=too-few-public-methods
+"A Square Implementer"
+from interface_shape_implementer import IShapeImplementer
+
+
+class SquareImplementer(IShapeImplementer):
+ "A Square Implementer"
+
+ def draw_implementation(self):
+ print("**************")
+ print("* *")
+ print("* *")
+ print("* *")
+ print("* *")
+ print("* *")
+ print("* *")
+ print("**************")
diff --git a/builder/Builder Design Pattern.md b/builder/Builder Design Pattern.md
deleted file mode 100644
index 7e24bd7..0000000
--- a/builder/Builder Design Pattern.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# Builder Design Pattern
-
-The Builder Pattern is a creational pattern whose intent is to separate the construction of a complex object from its representation so that you can use the same construction process to create different representations.
-
-The Builder Pattern tries to solve,
-- How can a class create different representations of a complex object?
-- How can a class that includes creating a complex object be simplified?
-
-The Builder and Factory patterns are very simiar in the fact they both instantiate new objects at run time. The difference is when the process of creating the object is more complex, so rather than the Factory returning a new instance of `ObjectA`, it could call the builders director construct method `ObjectA.construct()`. Both return an Object.
-
-Parts of the Builder Pattern
-1. **Product** - The Product being built
-2. **Concrete Builder** - Build the concrete product. Implements the IBuilder interface
-3. **Builder Interface** - The Interface which the Concrete builder should implement
-4. **Director** - Has a construct method which when called creates a customised product
-
-
-
-The Builder Pattern in the context of a House Builder. There are multiple directors creating there own complex objects
-
-.
-
diff --git a/builder/Builder Design Pattern.pdf b/builder/Builder Design Pattern.pdf
deleted file mode 100644
index b68d742..0000000
Binary files a/builder/Builder Design Pattern.pdf and /dev/null differ
diff --git a/builder/README.md b/builder/README.md
index 05938d6..1016ae9 100644
--- a/builder/README.md
+++ b/builder/README.md
@@ -1,21 +1,116 @@
# Builder Design Pattern
-The Builder Pattern is a creational pattern whose intent is to separate the construction of a complex object from its representation so that you can use the same construction process to create different representations.
+## Videos
-The Builder Pattern tries to solve,
-- How can a class create different representations of a complex object?
-- How can a class that includes creating a complex object be simplified?
+Section | Video Links
+-|-
+Builder Overview |
+Builder Use Case |
+Python **List** |
-The Builder and Factory patterns are very similar in the fact they both instantiate new objects at run time. The difference is when the process of creating the object is more complex, so rather than the Factory returning a new instance of `ObjectA`, it could call the builders director construct method `ObjectA.construct()`. Both return an Object.
+## Book
-Parts of the Builder Pattern
-1. **Product** - The Product being built
-2. **Concrete Builder** - Build the concrete product. Implements the IBuilder interface
-3. **Builder Interface** - The Interface which the Concrete builder should implement
-4. **Director** - Has a construct method which when called creates a customised product
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
-
+## Overview
-The Builder Pattern in the context of a House Builder. There are multiple directors creating there own complex objects
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
-.
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Builder UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Output
+
+``` bash
+python ./builder/builder_concept.py
+['a', 'b', 'c']
+```
+
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./builder/client.py
+This is a Ice Igloo with 1 door(s) and 0 window(s).
+This is a Sandstone Castle with 100 door(s) and 200 window(s).
+This is a Wood House Boat with 6 door(s) and 8 window(s).
+```
+
+## New Coding Concepts
+
+### Python List
+
+In the file [/builder/builder_concept.py](/builder/builder_concept.py)
+
+``` python linenums="47"
+
+ def __init__(self):
+ self.parts = []
+
+```
+
+The `[]` is indicating a Python **List**.
+
+The list can store multiple items, they can be changed, they can have items added and removed, can be re-ordered, can be pre-filled with items when instantiated and is also very flexible.
+
+``` python
+PS> python
+>>> items = []
+>>> items.append("shouldn't've")
+>>> items.append("y'aint")
+>>> items.extend(["whomst", "superfluity"])
+>>> items
+["shouldn't've", "y'aint", 'whomst', 'superfluity']
+>>> items.reverse()
+>>> items
+['superfluity', 'whomst', "y'aint", "shouldn't've"]
+>>> items.remove("y'aint")
+>>> items
+['superfluity', 'whomst', "shouldn't've"]
+>>> items.insert(1, "phoque")
+>>> items
+['superfluity', 'phoque', 'whomst', "shouldn't've"]
+>>> items.append("whomst")
+>>> items.count("whomst")
+2
+>>> len(items)
+5
+>>> items[2] = "bagnose"
+>>> items
+['superfluity', 'phoque', 'bagnose', "shouldn't've", 'whomst']
+>>> items[-2]
+"shouldn't've"
+```
+
+Lists are used in almost every code example in this book. You will see all the many ways they can be used.
+
+In fact, a list was used in the [/abstract_factory/furniture_factory.py](/abstract_factory/furniture_factory.py) example,
+
+``` python
+if furniture in ['SmallChair', 'MediumChair', 'BigChair']:
+ ...
+```
+
+This line, creates a list at runtime including the strings 'SmallChair', 'MediumChair' and 'BigChair'. If the value in `furniture` equals the same string as one of those items in the list, then the condition is true and the code within the if statement block will execute.
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/builder/builder.png b/builder/builder.png
deleted file mode 100644
index cf194b1..0000000
Binary files a/builder/builder.png and /dev/null differ
diff --git a/builder/builder.py b/builder/builder.py
deleted file mode 100644
index 27e99c5..0000000
--- a/builder/builder.py
+++ /dev/null
@@ -1,124 +0,0 @@
-"""The Builder Pattern in Python
-
-The intent of the Builder design pattern is to separate the
-construction of a complex object from its representation.
-By doing so the same construction process can create different
-representations
-
-The Builder Pattern tries to solve,
-- How can a class (the same construction process) create different
-representations of a complex object?
-- How can a class that includes creating a complex object be simplified?
-"""
-from abc import ABCMeta, abstractstaticmethod
-
-
-class IHouseBuilder(metaclass=ABCMeta):
- """The Builder Interface"""
-
- @abstractstaticmethod
- def set_wall_material(value):
- """Set the wall_material"""
-
- @abstractstaticmethod
- def set_building_type(value):
- """Set the building_type"""
-
- @abstractstaticmethod
- def set_number_doors(value):
- """Set the number of doors"""
-
- @abstractstaticmethod
- def set_number_windows(value):
- """Set the number of windows"""
-
- @abstractstaticmethod
- def get_result():
- """Return the house"""
-
-
-class HouseBuilder(IHouseBuilder):
- """The Concrete Builder."""
-
- def __init__(self):
- self.house = House()
-
- def set_wall_material(self, value):
- self.house.wall_material = value
- return self
-
- def set_building_type(self, value):
- self.house.building_type = value
- return self
-
- def set_number_doors(self, value):
- self.house.doors = value
- return self
-
- def set_number_windows(self, value):
- self.house.windows = value
- return self
-
- def get_result(self):
- return self.house
-
-
-class House():
- """The Product"""
-
- def __init__(self, building_type="Apartment", doors=0, windows=0, wall_material="Brick"):
- #brick, wood, straw, ice
- self.wall_material = wall_material
- # Apartment, Bungalow, Caravan, Hut, Castle, Duplex, HouseBoat, Igloo
- self.building_type = building_type
- self.doors = doors
- self.windows = windows
-
- def __str__(self):
- return "This is a {0} {1} with {2} door(s) and {3} window(s).".format(
- self.wall_material, self.building_type, self.doors, self.windows
- )
-
-
-class IglooDirector:
- """The Director, building a different representation."""
- @staticmethod
- def construct():
- return HouseBuilder()\
- .set_building_type("Igloo")\
- .set_wall_material("Ice")\
- .set_number_doors(1)\
- .set_number_windows(0)\
- .get_result()
-
-
-class HouseBoatDirector:
- """The Director, building a different representation."""
- @staticmethod
- def construct():
- return HouseBuilder()\
- .set_building_type("House Boat")\
- .set_wall_material("Wooden")\
- .set_number_doors(6)\
- .set_number_windows(8)\
- .get_result()
-
-
-class CastleDirector:
- """The Director, building a different representation."""
- @staticmethod
- def construct():
- return HouseBuilder()\
- .set_building_type("Castle")\
- .set_wall_material("Granite")\
- .set_number_doors(100)\
- .set_number_windows(200).get_result()
-
-
-if __name__ == "__main__":
- IGLOO = IglooDirector.construct()
- HOUSE_BOAT = HouseBoatDirector.construct()
- CASTLE = CastleDirector.construct()
- print(IGLOO)
- print(HOUSE_BOAT)
- print(CASTLE)
diff --git a/builder/builder_concept.py b/builder/builder_concept.py
new file mode 100644
index 0000000..676f3ee
--- /dev/null
+++ b/builder/builder_concept.py
@@ -0,0 +1,75 @@
+# pylint: disable=too-few-public-methods
+# pylint: disable=arguments-differ
+"Builder Concept Sample Code"
+from abc import ABCMeta, abstractmethod
+
+
+class IBuilder(metaclass=ABCMeta):
+ "The Builder Interface"
+
+ @staticmethod
+ @abstractmethod
+ def build_part_a():
+ "Build part a"
+
+ @staticmethod
+ @abstractmethod
+ def build_part_b():
+ "Build part b"
+
+ @staticmethod
+ @abstractmethod
+ def build_part_c():
+ "Build part c"
+
+ @staticmethod
+ @abstractmethod
+ def get_result():
+ "Return the final product"
+
+
+class Builder(IBuilder):
+ "The Concrete Builder."
+
+ def __init__(self):
+ self.product = Product()
+
+ def build_part_a(self):
+ self.product.parts.append('a')
+ return self
+
+ def build_part_b(self):
+ self.product.parts.append('b')
+ return self
+
+ def build_part_c(self):
+ self.product.parts.append('c')
+ return self
+
+ def get_result(self):
+ return self.product
+
+
+class Product():
+ "The Product"
+
+ def __init__(self):
+ self.parts = []
+
+
+class Director:
+ "The Director, building a complex representation."
+
+ @staticmethod
+ def construct():
+ "Constructs and returns the final product"
+ return Builder()\
+ .build_part_a()\
+ .build_part_b()\
+ .build_part_c()\
+ .get_result()
+
+
+# The Client
+PRODUCT = Director.construct()
+print(PRODUCT.parts)
diff --git a/builder/castle_director.py b/builder/castle_director.py
new file mode 100644
index 0000000..0f6a81d
--- /dev/null
+++ b/builder/castle_director.py
@@ -0,0 +1,16 @@
+"A Director Class"
+from house_builder import HouseBuilder
+
+
+class CastleDirector: # pylint: disable=too-few-public-methods
+ "One of the Directors, that can build a complex representation."
+
+ @staticmethod
+ def construct():
+ "Constructs and returns the final product"
+ return HouseBuilder()\
+ .set_building_type("Castle")\
+ .set_wall_material("Sandstone")\
+ .set_number_doors(100)\
+ .set_number_windows(200)\
+ .get_result()
diff --git a/builder/classes_builder.dot b/builder/classes_builder.dot
deleted file mode 100644
index 0099b22..0000000
--- a/builder/classes_builder.dot
+++ /dev/null
@@ -1,14 +0,0 @@
-digraph "classes_builder" {
-charset="utf-8"
-rankdir=BT
-{rank=same; 0, 2}
-"0" [label="{ConcreteBuilder|product\l|build_part_a()\lbuild_part_b()\lbuild_part_c()\l}", shape="record"];
-"2" [label="{IBuilder|\l|build_part_a()\lbuild_part_b()\lbuild_part_c()\l}", shape="record"];
-"3" [label="{Product|part_a : str\lpart_b : int\lpart_c : float\l|}", shape="record"];
-"1" [label="{Director|\l|construct()\l}", shape="record"];
-"0" -> "2" [arrowhead="empty", arrowtail="none"];
-"3" -> "0" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="product", style="solid"];
-
-"2" -> "1" [arrowhead="odiamond", arrowtail="none"];
-
-}
diff --git a/builder/classes_house_builder.dot b/builder/classes_house_builder.dot
deleted file mode 100644
index 12639c4..0000000
--- a/builder/classes_house_builder.dot
+++ /dev/null
@@ -1,21 +0,0 @@
-digraph "classes_house_builder" {
-charset="utf-8"
-rankdir=BT
-{rank=same; HouseBuilder, IHouseBuilder}
-"CastleDirector" [label="{CastleDirector|\l|construct()\l}", shape="record"];
-"House" [label="{House (Product)|building_type : str\ldoors : int\lwall_material : str\lwindows : int\l|}", shape="record"];
-"HouseBoatDirector" [label="{HouseBoatDirector|\l|construct()\l}", shape="record"];
-"HouseBuilder" [label="{HouseBuilder|house\l|get_result()\lset_building_type()\lset_number_doors()\lset_number_windows()\lset_wall_material()\l}", shape="record"];
-"IHouseBuilder" [label="{IHouseBuilder|\l|get_result()\lset_building_type()\lset_number_doors()\lset_number_windows()\lset_wall_material()\l}", shape="record"];
-"IglooDirector" [label="{IglooDirector|\l|construct()\l}", shape="record"];
-
-"HouseBuilder" -> "IHouseBuilder" [arrowhead="empty", arrowtail="none"];
-"House" -> "HouseBuilder" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="house", style="solid"];
-
-
-
-"IHouseBuilder" -> "HouseBoatDirector" [arrowhead="odiamond", arrowtail="none"];
-"IHouseBuilder" -> "IglooDirector" [arrowhead="odiamond", arrowtail="none"];
-"IHouseBuilder" -> "CastleDirector" [arrowhead="odiamond", arrowtail="none"];
-
-}
diff --git a/builder/client.py b/builder/client.py
new file mode 100644
index 0000000..a596f97
--- /dev/null
+++ b/builder/client.py
@@ -0,0 +1,13 @@
+"House Builder Example Code"
+
+from igloo_director import IglooDirector
+from castle_director import CastleDirector
+from houseboat_director import HouseBoatDirector
+
+IGLOO = IglooDirector.construct()
+CASTLE = CastleDirector.construct()
+HOUSEBOAT = HouseBoatDirector.construct()
+
+print(IGLOO.construction())
+print(CASTLE.construction())
+print(HOUSEBOAT.construction())
diff --git a/builder/house.py b/builder/house.py
new file mode 100644
index 0000000..56b4815
--- /dev/null
+++ b/builder/house.py
@@ -0,0 +1,21 @@
+"The Product"
+
+
+class House(): # pylint: disable=too-few-public-methods
+ "The Product"
+
+ def __init__(self, building_type="Apartment", doors=0,
+ windows=0, wall_material="Brick"):
+ # brick, wood, straw, ice
+ self.wall_material = wall_material
+ # Apartment, Bungalow, Caravan, Hut, Castle, Duplex,
+ # HouseBoat, Igloo
+ self.building_type = building_type
+ self.doors = doors
+ self.windows = windows
+
+ def construction(self):
+ "Returns a string describing the construction"
+ return f"This is a {self.wall_material} "\
+ f"{self.building_type} with {self.doors} "\
+ f"door(s) and {self.windows} window(s)."
diff --git a/builder/house_builder.png b/builder/house_builder.png
deleted file mode 100644
index d672560..0000000
Binary files a/builder/house_builder.png and /dev/null differ
diff --git a/builder/house_builder.py b/builder/house_builder.py
new file mode 100644
index 0000000..f676019
--- /dev/null
+++ b/builder/house_builder.py
@@ -0,0 +1,29 @@
+"The Builder Class"
+from interface_house_builder import IHouseBuilder
+from house import House
+
+
+class HouseBuilder(IHouseBuilder):
+ "The House Builder."
+
+ def __init__(self):
+ self.house = House()
+
+ def set_building_type(self, building_type):
+ self.house.building_type = building_type
+ return self
+
+ def set_wall_material(self, wall_material):
+ self.house.wall_material = wall_material
+ return self
+
+ def set_number_doors(self, number):
+ self.house.doors = number
+ return self
+
+ def set_number_windows(self, number):
+ self.house.windows = number
+ return self
+
+ def get_result(self):
+ return self.house
diff --git a/builder/houseboat_director.py b/builder/houseboat_director.py
new file mode 100644
index 0000000..68acae0
--- /dev/null
+++ b/builder/houseboat_director.py
@@ -0,0 +1,16 @@
+"A Director Class"
+from house_builder import HouseBuilder
+
+
+class HouseBoatDirector: # pylint: disable=too-few-public-methods
+ "One of the Directors, that can build a complex representation."
+
+ @staticmethod
+ def construct():
+ "Constructs and returns the final product"
+ return HouseBuilder()\
+ .set_building_type("House Boat")\
+ .set_wall_material("Wood")\
+ .set_number_doors(6)\
+ .set_number_windows(8)\
+ .get_result()
diff --git a/builder/igloo_director.py b/builder/igloo_director.py
new file mode 100644
index 0000000..5dfd618
--- /dev/null
+++ b/builder/igloo_director.py
@@ -0,0 +1,18 @@
+"A Director Class"
+from house_builder import HouseBuilder
+
+
+class IglooDirector: # pylint: disable=too-few-public-methods
+ "One of the Directors, that can build a complex representation."
+
+ @staticmethod
+ def construct():
+ """Constructs and returns the final product
+ Note that in this IglooDirector, it has omitted the set_number_of
+ windows call since this Igloo will have no windows.
+ """
+ return HouseBuilder()\
+ .set_building_type("Igloo")\
+ .set_wall_material("Ice")\
+ .set_number_doors(1)\
+ .get_result()
diff --git a/builder/interface_house_builder.py b/builder/interface_house_builder.py
new file mode 100644
index 0000000..cf18079
--- /dev/null
+++ b/builder/interface_house_builder.py
@@ -0,0 +1,31 @@
+"The Builder Interface"
+from abc import ABCMeta, abstractmethod
+
+
+class IHouseBuilder(metaclass=ABCMeta):
+ "The House Builder Interface"
+
+ @staticmethod
+ @abstractmethod
+ def set_building_type(building_type):
+ "Build type"
+
+ @staticmethod
+ @abstractmethod
+ def set_wall_material(wall_material):
+ "Build material"
+
+ @staticmethod
+ @abstractmethod
+ def set_number_doors(number):
+ "Number of doors"
+
+ @staticmethod
+ @abstractmethod
+ def set_number_windows(number):
+ "Number of windows"
+
+ @staticmethod
+ @abstractmethod
+ def get_result():
+ "Return the final product"
diff --git a/chain_of_responsibility/Chain Of Responsibility Design Pattern.md b/chain_of_responsibility/Chain Of Responsibility Design Pattern.md
deleted file mode 100644
index dfc2ad2..0000000
--- a/chain_of_responsibility/Chain Of Responsibility Design Pattern.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# Chain of Responsibility Design Pattern
-
-Chain of responsibility pattern is a behavioural pattern used to achieve loose coupling
-in software design.
-In this example, a request from a client is passed to a chain of objects to process them.
-The objects in the chain will decide how to process them and/or pas them to the next in the chain.
-The objects can also modify the next in the chain if for example you wanted to run objects in a recursive manner.
-
-
-## Chain of Responsibility Diagram
-
-
-
-## Chain of Responsibility UML Diagram in the context of an ATM
-
-
-In the ATM example, the chain is created to dispense an amount of £50, then £20s and then £10s in order.
-The successor chain is hardcoded in the chain client.
-
-```python
-def __init__(self):
- # initialize the successor chain
- self.chain1 = Dispenser50()
- self.chain2 = Dispenser20()
- self.chain3 = Dispenser10()
-
- # set the chain of responsibility
- # The Client may compose chains once or
- # the hadler can set them dynamically at
- # handle time
- self.chain1.set_successor(self.chain2)
- self.chain2.set_successor(self.chain3)
-
-```
-You also have the option to set the next successor on logic at handle time.
-
-## Output
-```bash
-$ python atm.py
-Enter amount to withdrawal
-130
-Dispensing 2 £50 note
-Dispensing 1 £20 note
-Dispensing 1 £10 note
-Go spoil yourself
-```
-
diff --git a/chain_of_responsibility/Chain Of Responsibility Design Pattern.pdf b/chain_of_responsibility/Chain Of Responsibility Design Pattern.pdf
deleted file mode 100644
index b8e8485..0000000
Binary files a/chain_of_responsibility/Chain Of Responsibility Design Pattern.pdf and /dev/null differ
diff --git a/chain_of_responsibility/README.md b/chain_of_responsibility/README.md
index 4cfe5c7..23537c5 100644
--- a/chain_of_responsibility/README.md
+++ b/chain_of_responsibility/README.md
@@ -1,47 +1,114 @@
# Chain of Responsibility Design Pattern
-Chain of responsibility pattern is a behavioural pattern used to achieve loose coupling
-in software design.
-In this example, a request from a client is passed to a chain of objects to process them.
-The objects in the chain will decide how to process them and/or pass them to the next in the chain.
-The objects can also modify the next in the chain if for example you wanted to run objects in a recursive manner.
+## Videos
+Section | Video Links
+-|-
+Chain of Responsibility |
+Use Case |
+Python Floor Division |
+Accepting User Input |
-## Chain of Responsibility Diagram
-
+## Book
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
-## Chain of Responsibility UML Diagram in the context of an ATM
-
+## Overview
-In the ATM example, the chain is created to dispense an amount of £50, then £20s and then £10s in order.
-The successor chain is hard coded in the chain client.
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
-```python
-def __init__(self):
- # initialize the successor chain
- self.chain1 = Dispenser50()
- self.chain2 = Dispenser20()
- self.chain3 = Dispenser10()
+## Terminology
- # set the chain of responsibility
- # The Client may compose chains once or
- # the handler can set them dynamically at
- # handle time
- self.chain1.set_successor(self.chain2)
- self.chain2.set_successor(self.chain3)
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+## Chain of Responsibility UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Output
+
+``` bash
+python ./chain_of_responsibility/chain_of_responsibility_concept.py
+Successor1 payload = 1
+Successor2 payload = -1
+Successor2 payload = -0.5
+Successor2 payload = -0.25
+Successor1 payload = -0.5
+Successor1 payload = 0.5
+Successor2 payload = -1.5
+Finished result = -1.5
```
-You also have the option to set the next successor on logic at handle time.
+
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Example UML Diagram
+
+
## Output
-```bash
-$ python atm.py
-Enter amount to withdrawal
-130
-Dispensing 2 £50 note
-Dispensing 1 £20 note
+
+``` bash
+python ./chain_of_responsibility/client.py
+Enter amount to withdrawal : 180
+Dispensing 3 £50 note(s)
+Dispensing 1 £20 note(s)
Dispensing 1 £10 note
-Go spoil yourself
+Now go spoil yourself
+```
+
+## New Coding Concepts
+
+### Floor Division
+
+Normally division uses a single **/** character and will return a float even if the numbers are integers or exactly divisible with no remainder,
+
+E.g.,
+
+``` python
+PS> python
+>>> 9 / 3
+3.0
+```
+
+Python Version 3 also has an option to return an integer version (floor) of the number by using the double **//** characters instead.
+
+``` python
+PS> python
+>>> 9 // 3
+3
+```
+
+See PEP-0238 : [https://www.python.org/dev/peps/pep-0238/](https://www.python.org/dev/peps/pep-0238/)
+
+### Accepting User Input
+
+In the file [/chain_of_responsibility/client.py](/chain_of_responsibility/client.py) above, there is a command `input` .
+
+The `input` command allows your script to accept user input from the command prompt.
+
+In the ATM example, when you start it, it will ask the user to enter a number.
+
+Then when the user presses the `enter` key, the input is converted to an integer and the value tested if valid.
+
+``` python
+AMOUNT = int(input("Enter amount to withdrawal : "))
+if AMOUNT < 10 or AMOUNT % 10 != 0:
+ ...continue
+
```
+Note that in Python 2.x, use the `raw_input()` command instead of `input()` .
+
+See PEP-3111 : [https://www.python.org/dev/peps/pep-3111/](https://www.python.org/dev/peps/pep-3111/)
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/chain_of_responsibility/atm.png b/chain_of_responsibility/atm.png
deleted file mode 100644
index 7ffb880..0000000
Binary files a/chain_of_responsibility/atm.png and /dev/null differ
diff --git a/chain_of_responsibility/atm.py b/chain_of_responsibility/atm.py
deleted file mode 100644
index 2a6ecfb..0000000
--- a/chain_of_responsibility/atm.py
+++ /dev/null
@@ -1,126 +0,0 @@
-"""Chain of Responsibility Pattern
-Chain of responsibility pattern is a behavioural pattern used to achieve loose coupling
-in software design.
-In this example, a request from a client is passed to a chain of objects to process them.
-The objects in the chain will decide how to process them and/or pas them to the next in the chain.
-The objects can also modify the next in the chain if for example you wanted to run objects in a recursive manner.
-"""
-
-from abc import ABCMeta, abstractstaticmethod
-
-
-class IHandler(metaclass=ABCMeta):
- """The Interface for handling requests."""
-
- @abstractstaticmethod
- def set_successor(successor):
- """Set the next handler in the chain"""
-
- @abstractstaticmethod
- def handle(amount):
- """Handle the event"""
-
-
-class Dispenser50(IHandler):
- """ConcreteHandler
- Dispense £50 notes if applicable,
- otherwise continue to successor
- """
-
- def __init__(self):
- self._successor = None
-
- def set_successor(self, successor):
- """Set the successor"""
- self._successor = successor
-
- def handle(self, amount):
- """Handle the dispensing of notes"""
- if amount >= 50:
- num = amount // 50
- remainder = amount % 50
- print(f"Dispensing {num} £50 note")
- if remainder != 0:
- self._successor.handle(remainder)
- else:
- self._successor.handle(amount)
-
-
-class Dispenser20(IHandler):
- """ConcreteHandler
- Dispense £20 notes if applicable,
- otherwise continue to successor
- """
-
- def __init__(self):
- self._successor = None
-
- def set_successor(self, successor):
- """Set the successor"""
- self._successor = successor
-
- def handle(self, amount):
- """Handle the dispensing of notes"""
- if amount >= 20:
- num = amount // 20
- remainder = amount % 20
- print(f"Dispensing {num} £20 note")
- if remainder != 0:
- self._successor.handle(remainder)
- else:
- self._successor.handle(amount)
-
-
-class Dispenser10(IHandler):
- """ConcreteHandler
- Dispense £10 notes if applicable,
- otherwise continue to successor
- """
-
- def __init__(self):
- self._successor = None
-
- def set_successor(self, successor):
- """Set the successor"""
- self._successor = successor
-
- def handle(self, amount):
- """Handle the dispensing of notes"""
- if amount >= 10:
- num = amount // 10
- remainder = amount % 10
- print(f"Dispensing {num} £10 note")
- if remainder != 0:
- self._successor.handle(remainder)
- else:
- self._successor.handle(amount)
-
-
-class ATMDispenserChain: # pylint: disable=too-few-public-methods
- """The Chain Client"""
-
- def __init__(self):
- # initialize the successor chain
- self.chain1 = Dispenser50()
- self.chain2 = Dispenser20()
- self.chain3 = Dispenser10()
-
- # set the chain of responsibility
- # The Client may compose chains once or
- # the hadler can set them dynamically at
- # handle time
- self.chain1.set_successor(self.chain2)
- self.chain2.set_successor(self.chain3)
-
-
-if __name__ == "__main__":
-
- ATM = ATMDispenserChain()
-
- AMOUNT = int(input("Enter amount to withdrawal : "))
- if AMOUNT < 10 or AMOUNT % 10 != 0:
- print("Amount should be positive and in multiple of 10s.")
- exit()
- # process the request
- ATM.chain1.handle(AMOUNT)
- print("Now go spoil yourself")
diff --git a/chain_of_responsibility/atm_dispenser_chain.py b/chain_of_responsibility/atm_dispenser_chain.py
new file mode 100644
index 0000000..3c3ada0
--- /dev/null
+++ b/chain_of_responsibility/atm_dispenser_chain.py
@@ -0,0 +1,19 @@
+"The ATM Dispenser Chain"
+from dispenser10 import Dispenser10
+from dispenser20 import Dispenser20
+from dispenser50 import Dispenser50
+
+
+class ATMDispenserChain: # pylint: disable=too-few-public-methods
+ "The Chain Client"
+
+ def __init__(self):
+ # initializing the successors chain
+ self.chain1 = Dispenser50()
+ self.chain2 = Dispenser20()
+ self.chain3 = Dispenser10()
+ # Setting a default successor chain that will process the 50s first,
+ # the 20s second and the 10s last. The successor chain will be
+ # recalculated dynamically at runtime.
+ self.chain1.next_successor(self.chain2)
+ self.chain2.next_successor(self.chain3)
diff --git a/chain_of_responsibility/chain_of_responsibility.png b/chain_of_responsibility/chain_of_responsibility.png
deleted file mode 100644
index d6e01ed..0000000
Binary files a/chain_of_responsibility/chain_of_responsibility.png and /dev/null differ
diff --git a/chain_of_responsibility/chain_of_responsibility_concept.py b/chain_of_responsibility/chain_of_responsibility_concept.py
new file mode 100644
index 0000000..863983d
--- /dev/null
+++ b/chain_of_responsibility/chain_of_responsibility_concept.py
@@ -0,0 +1,57 @@
+# pylint: disable=too-few-public-methods
+"The Chain Of Responsibility Pattern Concept"
+import random
+from abc import ABCMeta, abstractmethod
+
+
+class IHandler(metaclass=ABCMeta):
+ "The Handler Interface that the Successors should implement"
+ @staticmethod
+ @abstractmethod
+ def handle(payload):
+ "A method to implement"
+
+
+class Successor1(IHandler):
+ "A Concrete Handler"
+ @staticmethod
+ def handle(payload):
+ print(f"Successor1 payload = {payload}")
+ test = random.randint(1, 2)
+ if test == 1:
+ payload = payload + 1
+ payload = Successor1().handle(payload)
+ if test == 2:
+ payload = payload - 1
+ payload = Successor2().handle(payload)
+ return payload
+
+
+class Successor2(IHandler):
+ "A Concrete Handler"
+ @staticmethod
+ def handle(payload):
+ print(f"Successor2 payload = {payload}")
+ test = random.randint(1, 3)
+ if test == 1:
+ payload = payload * 2
+ payload = Successor1().handle(payload)
+ if test == 2:
+ payload = payload / 2
+ payload = Successor2().handle(payload)
+ return payload
+
+
+class Chain():
+ "A chain with a default first successor"
+ @staticmethod
+ def start(payload):
+ "Setting the first successor that will modify the payload"
+ return Successor1().handle(payload)
+
+
+# The Client
+CHAIN = Chain()
+PAYLOAD = 1
+OUT = CHAIN.start(PAYLOAD)
+print(f"Finished result = {OUT}")
diff --git a/chain_of_responsibility/classes_atm.dot b/chain_of_responsibility/classes_atm.dot
deleted file mode 100644
index c715a6d..0000000
--- a/chain_of_responsibility/classes_atm.dot
+++ /dev/null
@@ -1,15 +0,0 @@
-digraph "classes_atm" {
-charset="utf-8"
-rankdir=BT
-"0" [label="{ATMDispenserChain|chain1\lchain2\lchain3\l|}", shape="record"];
-"1" [label="{Dispenser10|\l|handle()\lset_successor()\l}", shape="record"];
-"2" [label="{Dispenser20|\l|handle()\lset_successor()\l}", shape="record"];
-"3" [label="{Dispenser50|\l|handle()\lset_successor()\l}", shape="record"];
-"4" [label="{IHandler|\l|handle()\lset_successor()\l}", shape="record"];
-"1" -> "4" [arrowhead="empty", arrowtail="none"];
-"2" -> "4" [arrowhead="empty", arrowtail="none"];
-"3" -> "4" [arrowhead="empty", arrowtail="none"];
-"1" -> "0" [arrowhead="ediamond", arrowtail="none", fontcolor="green", label="chain3", style="solid"];
-"2" -> "0" [arrowhead="ediamond", arrowtail="none", fontcolor="green", label="chain2", style="solid"];
-"3" -> "0" [arrowhead="ediamond", arrowtail="none", fontcolor="green", label="chain1", style="solid"];
-}
diff --git a/chain_of_responsibility/classes_chain_of_responsibility.dot b/chain_of_responsibility/classes_chain_of_responsibility.dot
deleted file mode 100644
index 0dc12c5..0000000
--- a/chain_of_responsibility/classes_chain_of_responsibility.dot
+++ /dev/null
@@ -1,19 +0,0 @@
-digraph R {
-
- node [shape=record];
-
- { rank=same Next1 Process1 }
- { rank=same Next2 Process2 }
- { rank=same Next3 Process3 }
-
- "Client"-> Next1;
-
- Next1 -> Process1;
- Process1 -> Next2;
- Next2 -> Process2;
- Process2 -> Next3
- Next3 -> Process3;
- Process3 -> Finished
- "Finished"
-
-}
\ No newline at end of file
diff --git a/chain_of_responsibility/client.py b/chain_of_responsibility/client.py
new file mode 100644
index 0000000..759bdcf
--- /dev/null
+++ b/chain_of_responsibility/client.py
@@ -0,0 +1,12 @@
+"An ATM Dispenser that dispenses denominations of notes"
+import sys
+from atm_dispenser_chain import ATMDispenserChain
+
+ATM = ATMDispenserChain()
+AMOUNT = int(input("Enter amount to withdrawal : "))
+if AMOUNT < 10 or AMOUNT % 10 != 0:
+ print("Amount should be positive and in multiple of 10s.")
+ sys.exit()
+# process the request
+ATM.chain1.handle(AMOUNT)
+print("Now go spoil yourself")
diff --git a/chain_of_responsibility/dispenser10.py b/chain_of_responsibility/dispenser10.py
new file mode 100644
index 0000000..112ce8a
--- /dev/null
+++ b/chain_of_responsibility/dispenser10.py
@@ -0,0 +1,24 @@
+"A dispenser of £10 notes"
+from interface_dispenser import IDispenser
+
+
+class Dispenser10(IDispenser):
+ "Dispenses £10s if applicable, otherwise continues to next successor"
+
+ def __init__(self):
+ self._successor = None
+
+ def next_successor(self, successor):
+ "Set the next successor"
+ self._successor = successor
+
+ def handle(self, amount):
+ "Handle the dispensing of notes"
+ if amount >= 10:
+ num = amount // 10
+ remainder = amount % 10
+ print(f"Dispensing {num} £10 note")
+ if remainder != 0:
+ self._successor.handle(remainder)
+ else:
+ self._successor.handle(amount)
diff --git a/chain_of_responsibility/dispenser20.py b/chain_of_responsibility/dispenser20.py
new file mode 100644
index 0000000..2b1d691
--- /dev/null
+++ b/chain_of_responsibility/dispenser20.py
@@ -0,0 +1,24 @@
+"A dispenser of £20 notes"
+from interface_dispenser import IDispenser
+
+
+class Dispenser20(IDispenser):
+ "Dispenses £20s if applicable, otherwise continues to next successor"
+
+ def __init__(self):
+ self._successor = None
+
+ def next_successor(self, successor):
+ "Set the next successor"
+ self._successor = successor
+
+ def handle(self, amount):
+ "Handle the dispensing of notes"
+ if amount >= 20:
+ num = amount // 20
+ remainder = amount % 20
+ print(f"Dispensing {num} £20 note(s)")
+ if remainder != 0:
+ self._successor.handle(remainder)
+ else:
+ self._successor.handle(amount)
diff --git a/chain_of_responsibility/dispenser50.py b/chain_of_responsibility/dispenser50.py
new file mode 100644
index 0000000..0592351
--- /dev/null
+++ b/chain_of_responsibility/dispenser50.py
@@ -0,0 +1,24 @@
+"A dispenser of £50 notes"
+from interface_dispenser import IDispenser
+
+
+class Dispenser50(IDispenser):
+ "Dispenses £50s if applicable, otherwise continues to next successor"
+
+ def __init__(self):
+ self._successor = None
+
+ def next_successor(self, successor):
+ "Set the next successor"
+ self._successor = successor
+
+ def handle(self, amount):
+ "Handle the dispensing of notes"
+ if amount >= 50:
+ num = amount // 50
+ remainder = amount % 50
+ print(f"Dispensing {num} £50 note(s)")
+ if remainder != 0:
+ self._successor.handle(remainder)
+ else:
+ self._successor.handle(amount)
diff --git a/chain_of_responsibility/interface_dispenser.py b/chain_of_responsibility/interface_dispenser.py
new file mode 100644
index 0000000..998c368
--- /dev/null
+++ b/chain_of_responsibility/interface_dispenser.py
@@ -0,0 +1,15 @@
+"The ATM Notes Dispenser Interface"
+from abc import ABCMeta, abstractmethod
+
+
+class IDispenser(metaclass=ABCMeta):
+ "Methods to implement"
+ @staticmethod
+ @abstractmethod
+ def next_successor(successor):
+ """Set the next handler in the chain"""
+
+ @staticmethod
+ @abstractmethod
+ def handle(amount):
+ """Handle the event"""
diff --git a/coding-conventions.md b/coding-conventions.md
new file mode 100644
index 0000000..5c8de04
--- /dev/null
+++ b/coding-conventions.md
@@ -0,0 +1,112 @@
+# Coding Conventions
+
+## Python Interactive Console Versus *.py Scripts
+
+You can execute Python code by writing your scripts into text files and commonly using the `.py` extension. Text files on most operating systems will be UTF-8 encoded by default. Python also reads UTF-8 encoded text files by default.
+
+Create a new text file called `example.py` and add the following text.
+
+``` python
+print("Hello World!")
+```
+
+and then you can execute it using `python` or `python3` depending on your operating system and Python version.
+
+``` bash
+PS> python ./example.py
+Hello World!
+```
+
+You can also enter Python code directly into the Python Interactive Console by typing just `python` or `python3` from the command line and then press `Enter` . You then get a prompt like below.
+
+``` python
+PS> python
+Python 3.10.1 (tags/v3.10.1:2cd268a, Dec 6 2021, 19:10:37) [MSC ...
+Type "help", "copyright", "credits" or "license" for more information.
+>>>
+```
+
+You can now enter python commands directly.
+
+```
+
+>>> print("Hello World!")
+Hello World!
+>>>
+```
+
+To exit the Python Interactive Console, you can usually type `quit()` or press `Ctrl-Z` then press `Enter`
+
+This repository will show examples of using both `*.py` scripts and the interactive console to execute Python. Look out for the `>>>` characters in the code blocks to indicate if I was using the Python Interactive Console or a `*.py` script.
+
+## PEP8
+
+The code styling in this repository is formatted using mostly PEP8 styling recommendations.
+
+* **UPPER_CASE** : Constants will be defined using UPPER_CASE naming style.
+* **PascalCase** : Class names will use PascalCase naming style
+* **snake_case** : For variables names, method names and method arguments.
+* **Docstrings** : Classes and methods contain extra documentation that is descriptive text enclosed in " or """ for multiline strings.
+* **_leading_underscore** : Use a leading underscore to indicate variables that should be considered as private class variables.
+
+See PEP-0008 : [https://www.python.org/dev/peps/pep-0008/](https://www.python.org/dev/peps/pep-0008/)
+
+## Pylint
+
+I use the Pylint tool to check for code styling recommendations.
+
+On most operating systems you would generally install Pylint by using the `PIP` or `PIP3` installer.
+
+``` powershell
+pip install pylint
+```
+
+If using VSCode, open the Command Palette (Ctrl+Shift+P), then set the
+
+`Python: Enable Linting` to `on`
+
+and
+
+`Python: Select Linter` to `Pylint`
+
+## Common Pylint Warning and Error Messages
+
+| ID | Message | Description |
+|-|-|-|
+| R0201 | Method could be a function (no-self-use)
+Command Use Case |
+Single Leading Underscore |
-Eg, a button, will call the Invoker, which will call a pre registered Commands execute method, which the Receiver will perform.
+## Book
-A Concrete Class will delegate a request to a command object, instead of implementing the request directly.
-Using a command design pattern allows you to separate concerns a little easier and to solve problems of the concerns independently of each of the layers.
-eg, logging the execution of a command and it's outcome.
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
-Uses:
-GUI Buttons, menus
-Macro recording
-Multi level undo/redo
-networking - send whole command objects across a network, even as a batch
-parallel processing or thread pools,
-transactional behaviour
-Wizards
+## Overview
-Notes:
-The receiver object should manages it's own state, not the command object
-There can be one or more invokers which can execute the command at a later date.
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
-
+## Terminology
-The Command Pattern in the context of a light switch
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
-
+## Command Pattern UML Diagram
-The Command Pattern in the context of a slider for a heater, which also implements UNDO/REDO
+
-
+## Source Code
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+## Output
+
+``` bash
+python ./command/command_concept.py
+Executing Command 1
+Executing Command 2
+Executing Command 1
+Executing Command 2
+```
+
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./command/client.py
+Light turned ON
+Light turned OFF
+Light turned ON
+Light turned OFF
+11:23:35 : ON
+11:23:35 : OFF
+11:23:35 : ON
+11:23:35 : OFF
+Light turned ON
+Light turned OFF
+```
+
+## New Coding Concepts
+
+### _Single Leading Underscore
+
+The single leading underscore **`_variable`**, on your class variables is a useful indicator to other developers that this property should be considered private.
+
+Private, in C style languages, means that the variable/field/property is hidden and cannot be accessed outside of the class. It can only be used internally by its own class methods.
+
+Python does not have a public/private accessor concept so the variable is not actually private and can still be used outside of the class in other modules.
+
+It is just a useful construct that you will see developers use as a recommendation not to reference this variable directly outside of this class, but use a dedicated method or property instead.
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/command/classes_command.dot b/command/classes_command.dot
deleted file mode 100644
index 6f4c4ae..0000000
--- a/command/classes_command.dot
+++ /dev/null
@@ -1,26 +0,0 @@
-digraph "classes_switch_command" {
-charset="utf-8"
-rankdir=BT
-{rank=same; Client, Invoker, ICommand}
-"Command1" [label="{Command1|\l|execute()\l}", shape="record"];
-"Command2" [label="{Command2|\l|execute()\l}", shape="record"];
-"Reciever" [label="{Reciever|\l|do_command1()\ldo_command2()\l}", shape="record"];
-"ICommand" [label="{ICommand|\l|execute()\l}", shape="record"];
-"Invoker" [label="{Invoker|\l|register()\lexecute()\l}", shape="record"];
-"Client" [label="{Client|\l|\l}", shape="record"];
-
-
-"Client" -> "Invoker" [arrowhead="open", arrowtail="none", style="dashed"];
-
-"Invoker" -> "ICommand" [arrowhead="open", arrowtail="none", style="dashed"];
-
-"Command1" -> "Reciever" [arrowhead="open", arrowtail="none", style="dashed"];
-"Command2" -> "Reciever" [arrowhead="open", arrowtail="none", style="dashed"];
-
-"Command1" -> "ICommand" [arrowhead="empty", arrowtail="none"];
-"Command2" -> "ICommand" [arrowhead="empty", arrowtail="none"];
-
-
-
-
-}
diff --git a/command/classes_slider.dot b/command/classes_slider.dot
deleted file mode 100644
index d52c681..0000000
--- a/command/classes_slider.dot
+++ /dev/null
@@ -1,23 +0,0 @@
-digraph "classes_slider" {
-charset="utf-8"
-rankdir=BT
-{rank=same; Slider, ICommand, Heater}
-"IUndoRedo" [label="{{IUndoRedo (Invoker)}|\l|history()\lundo()\lredo()\l}", shape="record"];
-"Slider" [label="{Slider (Invoker)|history\l|execute()\lregister()\lredo()\lundo()\l}", shape="record"];
-//"Slider" [label="{Slider (Invoker)|\l|execute()\lregister()\l}", shape="record"];
-"ICommand" [label="{ICommand (Command)|\l|execute()\l}", shape="record"];
-"Heater" [label="{Heater (Receiver)|\l|set_to_max()\lset_to_percent()\lturn_off()\l}", shape="record"];
-
-"3" [label="{SliderMaxCommand|\l|execute()\l}", shape="record"];
-"4" [label="{SliderOffCommand|\l|execute()\l}", shape="record"];
-"5" [label="{SliderPercentCommand|\l|execute()\l}", shape="record"];
-"3" -> "ICommand" [arrowhead="empty", arrowtail="none"];
-"4" -> "ICommand" [arrowhead="empty", arrowtail="none"];
-"5" -> "ICommand" [arrowhead="empty", arrowtail="none"];
-
-"Slider" -> "ICommand" [arrowhead="open", arrowtail="none", style="dashed"];
-"ICommand" -> "Heater" [arrowhead="open", arrowtail="none", style="dashed"];
-
-"Slider" -> "IUndoRedo" [arrowhead="empty", arrowtail="none"];
-
-}
diff --git a/command/classes_switch_command.dot b/command/classes_switch_command.dot
deleted file mode 100644
index 644f05b..0000000
--- a/command/classes_switch_command.dot
+++ /dev/null
@@ -1,26 +0,0 @@
-digraph "classes_switch_command" {
-charset="utf-8"
-rankdir=BT
-{rank=same; Client, Invoker, ICommand}
-"Command1" [label="{Switch ON|\l|execute()\l}", shape="record"];
-"Command2" [label="{Switch OFF|\l|execute()\l}", shape="record"];
-"Reciever" [label="{Light|\l|turn_off()\lturn_on()\l}", shape="record"];
-"ICommand" [label="{ICommand|\l|execute()\l}", shape="record"];
-"Invoker" [label="{Switch|\l|register()\lexecute()\l}", shape="record"];
-"Client" [label="{APP.py|\l|\l}", shape="record"];
-
-
-"Client" -> "Invoker" [arrowhead="open", arrowtail="none", style="dashed"];
-
-"Invoker" -> "ICommand" [arrowhead="open", arrowtail="none", style="dashed"];
-
-"Command1" -> "Reciever" [arrowhead="open", arrowtail="none", style="dashed"];
-"Command2" -> "Reciever" [arrowhead="open", arrowtail="none", style="dashed"];
-
-"Command1" -> "ICommand" [arrowhead="empty", arrowtail="none"];
-"Command2" -> "ICommand" [arrowhead="empty", arrowtail="none"];
-
-
-
-
-}
diff --git a/command/client.py b/command/client.py
new file mode 100644
index 0000000..c4de602
--- /dev/null
+++ b/command/client.py
@@ -0,0 +1,29 @@
+"The Command Pattern Use Case Example. A smart light Switch"
+from light import Light
+from switch import Switch
+from switch_on_command import SwitchOnCommand
+from switch_off_command import SwitchOffCommand
+
+# Create a receiver
+LIGHT = Light()
+
+# Create Commands
+SWITCH_ON = SwitchOnCommand(LIGHT)
+SWITCH_OFF = SwitchOffCommand(LIGHT)
+
+# Register the commands with the invoker
+SWITCH = Switch()
+SWITCH.register("ON", SWITCH_ON)
+SWITCH.register("OFF", SWITCH_OFF)
+
+# Execute the commands that are registered on the Invoker
+SWITCH.execute("ON")
+SWITCH.execute("OFF")
+SWITCH.execute("ON")
+SWITCH.execute("OFF")
+
+# show history
+SWITCH.show_history()
+
+# replay last two executed commands
+SWITCH.replay_last(2)
diff --git a/command/command_concept.py b/command/command_concept.py
new file mode 100644
index 0000000..e16231b
--- /dev/null
+++ b/command/command_concept.py
@@ -0,0 +1,84 @@
+# pylint: disable=arguments-differ
+"The Command Pattern Concept"
+from abc import ABCMeta, abstractmethod
+
+
+class ICommand(metaclass=ABCMeta): # pylint: disable=too-few-public-methods
+ "The command interface, that all commands will implement"
+ @staticmethod
+ @abstractmethod
+ def execute():
+ "The required execute method that all command objects will use"
+
+
+class Invoker:
+ "The Invoker Class"
+
+ def __init__(self):
+ self._commands = {}
+
+ def register(self, command_name, command):
+ "Register commands in the Invoker"
+ self._commands[command_name] = command
+
+ def execute(self, command_name):
+ "Execute any registered commands"
+ if command_name in self._commands.keys():
+ self._commands[command_name].execute()
+ else:
+ print(f"Command [{command_name}] not recognised")
+
+
+class Receiver:
+ "The Receiver"
+
+ @staticmethod
+ def run_command_1():
+ "A set of instructions to run"
+ print("Executing Command 1")
+
+ @staticmethod
+ def run_command_2():
+ "A set of instructions to run"
+ print("Executing Command 2")
+
+
+class Command1(ICommand): # pylint: disable=too-few-public-methods
+ """A Command object, that implements the ICommand interface and
+ runs the command on the designated receiver"""
+
+ def __init__(self, receiver):
+ self._receiver = receiver
+
+ def execute(self):
+ self._receiver.run_command_1()
+
+
+class Command2(ICommand): # pylint: disable=too-few-public-methods
+ """A Command object, that implements the ICommand interface and
+ runs the command on the designated receiver"""
+
+ def __init__(self, receiver):
+ self._receiver = receiver
+
+ def execute(self):
+ self._receiver.run_command_2()
+
+
+# Create a receiver
+RECEIVER = Receiver()
+
+# Create Commands
+COMMAND1 = Command1(RECEIVER)
+COMMAND2 = Command2(RECEIVER)
+
+# Register the commands with the invoker
+INVOKER = Invoker()
+INVOKER.register("1", COMMAND1)
+INVOKER.register("2", COMMAND2)
+
+# Execute the commands that are registered on the Invoker
+INVOKER.execute("1")
+INVOKER.execute("2")
+INVOKER.execute("1")
+INVOKER.execute("2")
diff --git a/command/command_direct.dot b/command/command_direct.dot
deleted file mode 100644
index 50f6248..0000000
--- a/command/command_direct.dot
+++ /dev/null
@@ -1,11 +0,0 @@
-digraph "classes_switch_command" {
-charset="utf-8"
-rankdir=BT
-"Receiver" [label="{Reciever|\l|do_command()\l}", shape="record"];
-"Client" [label="{Client|\l|\l}", shape="record"];
-
-
-"Client" -> "Receiver" [arrowhead="open", arrowtail="none", style="dashed"];
-
-
-}
diff --git a/command/command_direct.png b/command/command_direct.png
deleted file mode 100644
index 10f0aa0..0000000
Binary files a/command/command_direct.png and /dev/null differ
diff --git a/command/command_pattern.png b/command/command_pattern.png
deleted file mode 100644
index 214d14c..0000000
Binary files a/command/command_pattern.png and /dev/null differ
diff --git a/command/interface_switch.py b/command/interface_switch.py
new file mode 100644
index 0000000..9ce9126
--- /dev/null
+++ b/command/interface_switch.py
@@ -0,0 +1,11 @@
+"The switch interface, that all commands will implement"
+from abc import ABCMeta, abstractmethod
+
+
+class ISwitch(metaclass=ABCMeta): # pylint: disable=too-few-public-methods
+ "The switch interface, that all commands will implement"
+
+ @staticmethod
+ @abstractmethod
+ def execute():
+ "The required execute method that all command objects will use"
diff --git a/command/light.py b/command/light.py
new file mode 100644
index 0000000..ea70514
--- /dev/null
+++ b/command/light.py
@@ -0,0 +1,15 @@
+"The Light. The Receiver"
+
+
+class Light:
+ "The Receiver"
+
+ @staticmethod
+ def turn_on():
+ "A set of instructions to run"
+ print("Light turned ON")
+
+ @staticmethod
+ def turn_off():
+ "A set of instructions to run"
+ print("Light turned OFF")
diff --git a/command/slider_command.png b/command/slider_command.png
deleted file mode 100644
index c5646f0..0000000
Binary files a/command/slider_command.png and /dev/null differ
diff --git a/command/slider_command.py b/command/slider_command.py
deleted file mode 100644
index 5b35d6a..0000000
--- a/command/slider_command.py
+++ /dev/null
@@ -1,180 +0,0 @@
-"""The Command Design Pattern in Python
-The command pattern is a behavioural design pattern, in which an abstraction
-exists between an object that invokes a command, and the object that performs it.
-
-This is part 2 of the Command Design Pattern tutorial,
-where I create a slider, instead of the switch from part 1.
-The slider also accepts a variable percentage, rather than an ON/OFF
-The history also records the variable settingg
-And I also add UNDO/REDO to the Invoker so that you can go backawards and forwards through time
-
-"""
-
-from abc import ABCMeta, abstractstaticmethod
-import time
-
-
-class ICommand(metaclass=ABCMeta):
- """The command interface, which all commands will implement"""
-
- @abstractstaticmethod
- def execute(*args):
- """The required execute method which all command objects will use"""
-
-
-class IUndoRedo(metaclass=ABCMeta):
- """The Undo Redo interface"""
- @abstractstaticmethod
- def history():
- """the history of the states"""
-
- @abstractstaticmethod
- def undo():
- """for undoing the hsitory of the states"""
-
- @abstractstaticmethod
- def redo():
- """for redoing the hsitory of the states"""
-
-
-class Slider(IUndoRedo):
- """The Invoker Class"""
-
- def __init__(self):
- self._commands = {}
- self._history = [(0.0, "OFF", ())] # A default setting of OFF
- self._history_position = 0 # The position that is used for UNDO/REDO
-
- @property
- def history(self):
- """Return all records in the History list"""
- return self._history
-
- def register(self, command_name, command):
- """All commands are registered in the Invoker Class"""
- self._commands[command_name] = command
-
- def execute(self, command_name, *args):
- """Execute a pre defined command and log in history"""
- if command_name in self._commands.keys():
- self._history_position += 1
- self._commands[command_name].execute(args)
- if len(self._history) == self._history_position:
- # This is a new event in hisory
- self._history.append((time.time(), command_name, args))
- else:
- # This occurs if there was one of more UNDOs and then a new
- # execute command happened. In case of UNDO, the history_position
- # changes, and executing new commands purges any history after
- # the current position"""
- self._history = self._history[:self._history_position+1]
- self._history[self._history_position] = {
- time.time(): [command_name, args]
- }
- else:
- print(f"Command [{command_name}] not recognised")
-
- def undo(self):
- """Undo a command if there is a command that can be undone.
- Update the history position so that further UNDOs or REDOs
- point to the correct index"""
- if self._history_position > 0:
- self._history_position -= 1
- self._commands[
- self._history[self._history_position][1]
- ].execute(self._history[self._history_position][2])
- else:
- print("nothing to undo")
-
- def redo(self):
- """Perform a REDO if the history_position is less than the end of the history list"""
- if self._history_position + 1 < len(self._history):
- self._history_position += 1
- self._commands[
- self._history[self._history_position][1]
- ].execute(self._history[self._history_position][2])
- else:
- print("nothing to REDO")
-
-
-class Heater:
- """The Receiver"""
-
- def set_to_max(self):
- print("Heater is ON and set to MAX (100%)")
-
- def set_to_percent(self, *args):
- print(f"Heater is ON and set to {args[0][0]}%")
-
- def turn_off(self):
- print("Heater is OFF")
-
-
-class SliderMaxCommand(ICommand):
- """A Command object, which implements the ICommand interface"""
-
- def __init__(self, heater):
- self._heater = heater
-
- def execute(self, *args):
- self._heater.set_to_max()
-
-
-class SliderPercentCommand(ICommand):
- """A Command object, which implements the ICommand interface"""
-
- def __init__(self, heater):
- self._heater = heater
-
- def execute(self, *args):
- self._heater.set_to_percent(args[0])
-
-
-class SliderOffCommand(ICommand):
- """A Command object, which implements the ICommand interface"""
-
- def __init__(self, heater):
- self._heater = heater
-
- def execute(self, *args):
- self._heater.turn_off()
-
-
-if __name__ == "__main__":
- # The Client is the main python app
-
- # The HEATER is the Receiver
- HEATER = Heater()
-
- # Create Commands
- SLIDER_MAX = SliderMaxCommand(HEATER)
- SLIDER_PERCENT = SliderPercentCommand(HEATER)
- SLIDER_OFF = SliderOffCommand(HEATER)
-
- # Register the commands with the invoker (Switch)
- SLIDER = Slider()
- SLIDER.register("MAX", SLIDER_MAX)
- SLIDER.register("PERCENT", SLIDER_PERCENT)
- SLIDER.register("OFF", SLIDER_OFF)
-
- # Execute the commands that are registered on the Invoker
- SLIDER.execute("PERCENT", 10)
- SLIDER.execute("PERCENT", 20)
- SLIDER.execute("PERCENT", 30)
- SLIDER.execute("PERCENT", 40)
- SLIDER.execute("PERCENT", 50)
- print(SLIDER.history)
- SLIDER.undo()
- SLIDER.undo()
- SLIDER.undo()
- SLIDER.redo()
- SLIDER.undo()
- SLIDER.undo()
- SLIDER.execute("PERCENT", 90)
- SLIDER.execute("MAX")
- SLIDER.execute("OFF")
- print(SLIDER.history)
- SLIDER.undo()
- SLIDER.redo()
-
- print(SLIDER.history)
diff --git a/command/switch.py b/command/switch.py
new file mode 100644
index 0000000..8b3a5fa
--- /dev/null
+++ b/command/switch.py
@@ -0,0 +1,42 @@
+"""
+The Switch (Invoker) Class.
+You can flick the switch and it then invokes a registered command
+"""
+from datetime import datetime
+import time
+
+
+class Switch:
+ "The Invoker Class."
+
+ def __init__(self):
+ self._commands = {}
+ self._history = []
+
+ def show_history(self):
+ "Print the history of each time a command was invoked"
+ for row in self._history:
+ print(
+ f"{datetime.fromtimestamp(row[0]).strftime('%H:%M:%S')}"
+ f" : {row[1]}"
+ )
+
+ def register(self, command_name, command):
+ "Register commands in the Invoker"
+ self._commands[command_name] = command
+
+ def execute(self, command_name):
+ "Execute any registered commands"
+ if command_name in self._commands.keys():
+ self._commands[command_name].execute()
+ self._history.append((time.time(), command_name))
+ else:
+ print(f"Command [{command_name}] not recognised")
+
+ def replay_last(self, number_of_commands):
+ "Replay the last N commands"
+ commands = self._history[-number_of_commands:]
+ for command in commands:
+ self._commands[command[1]].execute()
+ #or if you want to record these replays in history
+ #self.execute(command[1])
diff --git a/command/switch_command.py b/command/switch_command.py
deleted file mode 100644
index 4e5de0a..0000000
--- a/command/switch_command.py
+++ /dev/null
@@ -1,122 +0,0 @@
-"""The Command Design Pattern in Python
-The command pattern is a behavioural design pattern, in which an abstraction
-exists between an object that invokes a command, and the object that performs it.
-
-The components if the Command Design Pattern are,
-The Receiver - The Object that will receive and execute the command
-The Invoker - Which will send the command to the receiver
-The Command Object - Itself, which implements an execute, or action method,
-and contains all required information or module which is aware of
-the Reciever, Invoker and Commands
-
-Eg, a button, will call the Invoker, which will call a pre registered Commands excute method,
-which will perform the action on the Reciever.
-
-A Concrete Class will delegate a request to a command object, instead of
-implementing the request directly.
-Using a command design pattern allows you to seperate concerns a little easier and to
-solve problems of the concenrs independantly of each of the layers.
-eg, logging the execution of a command and it's outcome.
-
-Uses:
-GUI Buttons, menus
-Macro recording
-Multi level undo/redo
-networking - send whole command objects across a network, even as a batch
-parallel processing or thread pools,
-transactional behaviour
-Wizards
-
-Notes:
-The receiver object should manages it's own state, not the command object
-There can be one or more invokers which can execute the command at a later date.
-
-"""
-
-from abc import ABCMeta, abstractstaticmethod
-import time
-
-
-class ICommand(metaclass=ABCMeta):
- """The command interface, which all commands will implement"""
-
- @abstractstaticmethod
- def execute():
- """The required execute method which all command obejcts will use"""
-
-
-class Switch:
- """The Invoker Class"""
-
- def __init__(self):
- self._commands = {}
- self._history = []
-
- @property
- def history(self):
- return self._history
-
- def register(self, command_name, command):
- self._commands[command_name] = command
-
- def execute(self, command_name):
- if command_name in self._commands.keys():
- self._history.append((time.time(), command_name))
- self._commands[command_name].execute()
- else:
- print(f"Command [{command_name}] not recognised")
-
-
-class Light:
- """The Reciever"""
-
- def turn_on(self):
- print("Light turned ON")
-
- def turn_off(self):
- print("Light turned OFF")
-
-
-class SwitchOnCommand(ICommand):
- """A Command object, which implemets the ICommand interface"""
-
- def __init__(self, light):
- self._light = light
-
- def execute(self):
- self._light.turn_on()
-
-
-class SwitchOffCommand(ICommand):
- """A Command object, which implemets the ICommand interface"""
-
- def __init__(self, light):
- self._light = light
-
- def execute(self):
- self._light.turn_off()
-
-
-if __name__ == "__main__":
- # The Client is the main python app
-
- # The Light is the Reciever
- LIGHT = Light()
-
- # Create Commands
- SWITCH_ON = SwitchOnCommand(LIGHT)
- SWITCH_OFF = SwitchOffCommand(LIGHT)
-
- # Register the commands with the invoker (Switch)
- SWITCH = Switch()
- SWITCH.register("ON", SWITCH_ON)
- SWITCH.register("OFF", SWITCH_OFF)
-
- # Execute the commands that are registered on the Invoker
- SWITCH.execute("ON")
- SWITCH.execute("OFF")
- SWITCH.execute("ON")
- SWITCH.execute("OFF")
-
- # For fun, we can see the history
- print(SWITCH.history)
diff --git a/command/switch_command_pattern.png b/command/switch_command_pattern.png
deleted file mode 100644
index c5287f3..0000000
Binary files a/command/switch_command_pattern.png and /dev/null differ
diff --git a/command/switch_off_command.py b/command/switch_off_command.py
new file mode 100644
index 0000000..a496262
--- /dev/null
+++ b/command/switch_off_command.py
@@ -0,0 +1,15 @@
+"""
+A Command object, that implements the ISwitch interface and runs the
+command on the designated receiver
+"""
+from interface_switch import ISwitch
+
+
+class SwitchOffCommand(ISwitch): # pylint: disable=too-few-public-methods
+ "Switch Off Command"
+
+ def __init__(self, light):
+ self._light = light
+
+ def execute(self):
+ self._light.turn_off()
diff --git a/command/switch_on_command.py b/command/switch_on_command.py
new file mode 100644
index 0000000..be9255a
--- /dev/null
+++ b/command/switch_on_command.py
@@ -0,0 +1,15 @@
+"""
+A Command object, that implements the ISwitch interface and runs the
+command on the designated receiver
+"""
+from interface_switch import ISwitch
+
+
+class SwitchOnCommand(ISwitch): # pylint: disable=too-few-public-methods
+ "Switch On Command"
+
+ def __init__(self, light):
+ self._light = light
+
+ def execute(self):
+ self._light.turn_on()
diff --git a/composite/README.md b/composite/README.md
index 1f0a41e..7a9f7b0 100644
--- a/composite/README.md
+++ b/composite/README.md
@@ -1,16 +1,127 @@
# Composite Design Pattern
-The Composite design pattern,
-- allows you to represent individual entities and groups of entities in the same manner.
-- is a structural design pattern that lets you compose objects into a tree.
-- is great if you need the option of swapping hierarchal relationships around.
-- makes it easier for you to add new kinds of components
-- provides flexibility of structure
-- conform to the Single Responsibility Principle in the way that it separates the aggregation of objects from the features of the object.
+## Videos
-Examples of using the Composite Design Pattern can be seen in a filesystem directory structure, where you can swap the hierarchy of folders, and in a drawing program where you can group, un-group and transform objects, and multiple objects at the same time.
+Section | Video Links
+-|-
+Composite Overview |
+Composite Use Case |
+Conditional Expressions |
-
+## Book
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
+## Overview
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Composite UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Output
+
+``` bash
+python ./composite/composite_concept.py
+
+LEAF_A id:2050574298848
+LEAF_B id:2050574298656
+COMPOSITE_1 id:2050574298272
+COMPOSITE_2 id:2050574298128
+
+
+Decorator Use Case |
+**\__str\__** Dunder Method|
+Python **getattr()** Method |
-The decorator pattern is different than the Python language feature of Python Decorators in it's syntax, but the application of it is the same, in the way that it is essentially a wrapper.
+## Book
-The Decorator pattern adds extensibility, without modifying the original function.
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
-
\ No newline at end of file
+## Overview
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Builder UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./decorator/decorator_concept.py
+Component Method
+Decorator Method(Component Method)
+```
+
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./decorator/client.py
+3
+101
+4
+6
+-1
+106
+113
+114
+1
+2
+5
+```
+
+## New Coding Concepts
+
+### Python `getattr()` Function
+
+Syntax: `getattr(object, attribute, default)`
+
+In the `Sub` and `Add` classes, I use the `getattr()` method like a ternary operator.
+
+When initializing the `Add` or `Sub` classes, you have the option of providing an integer or an existing instance of the `Value`, `Sub` or `Add` classes.
+
+So, for example, the line in the `Sub` class,
+
+``` python
+
+val1 = getattr(val1, 'value', val1)
+```
+
+is saying, if the `val1` just passed into the function already has an attribute `value`, then `val1` must be an object of `Value`, `Sub` or `Add` . Otherwise, the `val1` that was passed in is a new integer and it will use that instead to calculate the final value of the instance on the next few lines of code. This behavior allows the `Sub` and `Add` classes to be used recursively.
+
+E.g.,
+
+``` python
+A = Value(2)
+Add(Sub(Add(200, 15), A), 100)
+```
+
+### Dunder `__str__` method
+
+When you `print()` an object, it will print out the objects type and memory location in hex.
+
+``` python
+class ExampleClass:
+ abc = 123
+
+print(ExampleClass())
+```
+
+Outputs
+
+``` text
+<__main__.ExampleClass object at 0x00000283038B1D00>
+```
+
+You can change this default output by implementing the `__str__` dunder method in your class. Dunder is short for saying double underscore.
+
+Dunder methods are predefined methods in python that you can override with your own implementations.
+
+``` python
+class ExampleClass:
+ abc = 123
+
+ def __str__(self):
+ return "Something different"
+
+print(ExampleClass())
+```
+
+Now outputs
+
+``` text
+Something different
+```
+
+In all the classes in the above use case example that implement the `IValue` interface, the `__str__` method is overridden to return a string version of the integer value. This allows to print the numerical value of any object that implements the `IValue` interface rather than printing a string that resembles something like below.
+
+``` bash
+<__main__.ValueClass object at 0x00000283038B1D00>
+```
+
+The `__str__` dunder was also overridden in the [/prototype/prototype_concept.py](/prototype/prototype_concept.py) concept code.
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/decorator/add.py b/decorator/add.py
new file mode 100644
index 0000000..2a74d97
--- /dev/null
+++ b/decorator/add.py
@@ -0,0 +1,17 @@
+# pylint: disable=too-few-public-methods
+"The Add Decorator"
+from interface_value import IValue
+
+
+class Add(IValue):
+ "A Decorator that Adds a number to a number"
+
+ def __init__(self, val1, val2):
+ # val1 and val2 can be int or the custom Value
+ # object that contains the `value` attribute
+ val1 = getattr(val1, 'value', val1)
+ val2 = getattr(val2, 'value', val2)
+ self.value = val1 + val2
+
+ def __str__(self):
+ return str(self.value)
diff --git a/decorator/client.py b/decorator/client.py
new file mode 100644
index 0000000..4b0807b
--- /dev/null
+++ b/decorator/client.py
@@ -0,0 +1,20 @@
+"Decorator Use Case Example Code"
+from value import Value
+from add import Add
+from sub import Sub
+
+A = Value(1)
+B = Value(2)
+C = Value(5)
+
+print(Add(A, B))
+print(Add(A, 100))
+print(Sub(C, A))
+print(Sub(Add(C, B), A))
+print(Sub(100, 101))
+print(Add(Sub(Add(C, B), A), 100))
+print(Sub(123, Add(C, C)))
+print(Add(Sub(Add(C, 10), A), 100))
+print(A)
+print(B)
+print(C)
diff --git a/decorator/decorator.dot b/decorator/decorator.dot
deleted file mode 100644
index a7e32a2..0000000
--- a/decorator/decorator.dot
+++ /dev/null
@@ -1,9 +0,0 @@
-digraph "classes" {
-charset="utf-8"
-rankdir=BT
-"0" [label="{UndecoratedObject|\l|get()\l}", shape="record"];
-"1" [label="{Decorate|decorated\l|get()\l}", shape="record"];
-"2" [label="{DecorateWithANewMethod|decorated\l|draw()\lget()\l}", shape="record"];
-
-
-}
diff --git a/decorator/decorator.png b/decorator/decorator.png
deleted file mode 100644
index 3be9c01..0000000
Binary files a/decorator/decorator.png and /dev/null differ
diff --git a/decorator/decorator.py b/decorator/decorator.py
deleted file mode 100644
index 03682d8..0000000
--- a/decorator/decorator.py
+++ /dev/null
@@ -1,35 +0,0 @@
-"""
-Decorator Design Pattern
-"""
-
-class UndecoratedObject:
- @staticmethod
- def get():
- return "UndecoratedObject"
-
-
-class Decorate:
- def __init__(self, undecorated):
- self.undecorated = undecorated
-
- def get(self):
- return self.undecorated.get().replace("Undecorated", "Decorated")
-
-
-# class DecorateWithANewMethod:
-# def __init__(self, undecorated):
-# self.undecorated = undecorated
-
-# def get(self):
-# return self.undecorated.get()
-
-# def draw(self):
-# print(self.undecorated.get())
-
-
-UNDECORATED = UndecoratedObject()
-print(UNDECORATED.get())
-DECORATED = Decorate(UNDECORATED)
-print(DECORATED.get())
-#DECORATEDWITHNEWMETHOD = DecorateWithANewMethod(DECORATED)
-#DECORATEDWITHNEWMETHOD.draw()
diff --git a/decorator/decorator_concept.py b/decorator/decorator_concept.py
new file mode 100644
index 0000000..617add5
--- /dev/null
+++ b/decorator/decorator_concept.py
@@ -0,0 +1,38 @@
+# pylint: disable=too-few-public-methods
+# pylint: disable=arguments-differ
+"Decorator Concept Sample Code"
+from abc import ABCMeta, abstractmethod
+
+
+class IComponent(metaclass=ABCMeta):
+ "Methods the component must implement"
+ @staticmethod
+ @abstractmethod
+ def method():
+ "A method to implement"
+
+
+class Component(IComponent):
+ "A component that can be decorated or not"
+
+ def method(self):
+ "An example method"
+ return "Component Method"
+
+
+class Decorator(IComponent):
+ "The Decorator also implements the IComponent"
+
+ def __init__(self, obj):
+ "Set a reference to the decorated object"
+ self.object = obj
+
+ def method(self):
+ "A method to implement"
+ return f"Decorator Method({self.object.method()})"
+
+
+# The Client
+COMPONENT = Component()
+print(COMPONENT.method())
+print(Decorator(COMPONENT).method())
diff --git a/decorator/interface_value.py b/decorator/interface_value.py
new file mode 100644
index 0000000..ae372d5
--- /dev/null
+++ b/decorator/interface_value.py
@@ -0,0 +1,11 @@
+# pylint: disable=too-few-public-methods
+"The Interface that Value should implement"
+from abc import ABCMeta, abstractmethod
+
+
+class IValue(metaclass=ABCMeta):
+ "Methods the component must implement"
+ @staticmethod
+ @abstractmethod
+ def __str__():
+ "Override the object to return the value attribute by default"
diff --git a/decorator/sub.py b/decorator/sub.py
new file mode 100644
index 0000000..36eb186
--- /dev/null
+++ b/decorator/sub.py
@@ -0,0 +1,17 @@
+# pylint: disable=too-few-public-methods
+"The Subtract Decorator"
+from interface_value import IValue
+
+
+class Sub(IValue):
+ "A Decorator that subtracts a number from a number"
+
+ def __init__(self, val1, val2):
+ # val1 and val2 can be int or the custom Value
+ # object that contains the `value` attribute
+ val1 = getattr(val1, 'value', val1)
+ val2 = getattr(val2, 'value', val2)
+ self.value = val1 - val2
+
+ def __str__(self):
+ return str(self.value)
diff --git a/decorator/value.py b/decorator/value.py
new file mode 100644
index 0000000..c330146
--- /dev/null
+++ b/decorator/value.py
@@ -0,0 +1,13 @@
+# pylint: disable=too-few-public-methods
+"The Custom Value class"
+from interface_value import IValue
+
+
+class Value(IValue):
+ "A component that can be decorated or not"
+
+ def __init__(self, value):
+ self.value = value
+
+ def __str__(self):
+ return str(self.value)
diff --git a/docs/CNAME b/docs/CNAME
deleted file mode 100644
index b6f0086..0000000
--- a/docs/CNAME
+++ /dev/null
@@ -1 +0,0 @@
-designpatterns.seanwasere.com
\ No newline at end of file
diff --git a/docs/abstract_factory.md b/docs/abstract_factory.md
deleted file mode 100644
index 2687321..0000000
--- a/docs/abstract_factory.md
+++ /dev/null
@@ -1,206 +0,0 @@
-# Abstract Factory Design Pattern
-
-## Video Lecture
-
-Skillshare : [https://skl.sh/34SM2Xg](https://skl.sh/34SM2Xg "Abstract Factory Design Pattern")
-
-Udemy : [Abstract Factory Design Pattern](https://www.udemy.com/course/design-patterns-in-python/learn/lecture/16396782/?referralCode=7B677DD7A9580F2FFD8F "Abstract Factory Design Pattern")
-
-## Description
-
-The Abstract Factory Pattern adds an abstract layer over multiple factory method implementations.
-
-The Abstract Factory contains or composites one or more than one factory method
-
-
-
-Abstract Factory in the context of a Furniture factory 
-
-## Source Code
-
-### **`chair_factory.py`**
-```python
-from abc import ABCMeta, abstractstaticmethod
-
-
-class IChair(metaclass=ABCMeta): # pylint: disable=too-few-public-methods
- """The Chair Interface"""
-
- @abstractstaticmethod
- def dimensions():
- """A static inteface method"""
-
-
-class BigChair(IChair): # pylint: disable=too-few-public-methods
- """The Big Chair Concrete Class which implements the IChair interface"""
-
- def __init__(self):
- self._height = 80
- self._width = 80
- self._depth = 80
-
- def dimensions(self):
- return {"width": self._width, "depth": self._depth, "height": self._height}
-
-
-class MediumChair(IChair): # pylint: disable=too-few-public-methods
- """The Medium Chair Concrete Class which implements the IChair interface"""
-
- def __init__(self):
- self._height = 60
- self._width = 60
- self._depth = 60
-
- def dimensions(self):
- return {"width": self._width, "depth": self._depth, "height": self._height}
-
-
-class SmallChair(IChair): # pylint: disable=too-few-public-methods
- """The Small Chair Concrete Class which implements the IChair interface"""
-
- def __init__(self):
- self._height = 40
- self._width = 40
- self._depth = 40
-
- def dimensions(self):
- return {"width": self._width, "depth": self._depth, "height": self._height}
-
-
-class ChairFactory: # pylint: disable=too-few-public-methods
- """Tha Factory Class"""
-
- @staticmethod
- def get_chair(chair):
- """A static method to get a table"""
- try:
- if chair == "BigChair":
- return BigChair()
- if chair == "MediumChair":
- return MediumChair()
- if chair == "SmallChair":
- return SmallChair()
- raise AssertionError("Chair Not Found")
- except AssertionError as _e:
- print(_e)
- return None
-
-
-if __name__ == "__main__":
- CHAIR_FACTORY = ChairFactory().get_chair("SmallChair")
- print(CHAIR_FACTORY.dimensions())
-```
-
-### **`table_factory.py`**
-```python
-from abc import ABCMeta, abstractstaticmethod
-
-
-class ITable(metaclass=ABCMeta): # pylint: disable=too-few-public-methods
- """The Table Interface"""
-
- @abstractstaticmethod
- def dimensions():
- """Get the table dimensions"""
-
-
-class BigTable(ITable): # pylint: disable=too-few-public-methods
- """The Big Table Concrete Class which implements the ITable interface"""
-
- def __init__(self):
- self._height = 60
- self._width = 120
- self._depth = 80
-
- def dimensions(self):
- return {"width": self._width, "depth": self._depth, "height": self._height}
-
-
-class MediumTable(ITable): # pylint: disable=too-few-public-methods
- """The Medium Table Concrete Class which implements the ITable interface"""
-
- def __init__(self):
- self._height = 60
- self._width = 110
- self._depth = 70
-
- def dimensions(self):
- return {"width": self._width, "depth": self._depth, "height": self._height}
-
-
-class SmallTable(ITable): # pylint: disable=too-few-public-methods
- """The Small Table Concrete Class which implements the ITable interface"""
-
- def __init__(self):
- self._height = 60
- self._width = 100
- self._depth = 60
-
- def dimensions(self):
- return {"width": self._width, "depth": self._depth, "height": self._height}
-
-
-class TableFactory: # pylint: disable=too-few-public-methods
- """Tha Factory Class"""
-
- @staticmethod
- def get_table(table):
- """A static method to get a table"""
- try:
- if table == "BigTable":
- return BigTable()
- if table == "MediumTable":
- return MediumTable()
- if table == "SmallTable":
- return SmallTable()
- raise AssertionError("Table Not Found")
- except AssertionError as _e:
- print(_e)
- return None
-
-
-if __name__ == "__main__":
- TABLE = TableFactory().get_table("SmalTable")
- print(TABLE)
-
-```
-
-### **`furniture_abstract_factory.py`**
-```python
-from abc import ABCMeta, abstractstaticmethod
-from chair_factory import ChairFactory
-from table_factory import TableFactory
-
-
-class IFurnitureFactory(metaclass=ABCMeta): # pylint: disable=too-few-public-methods
- """Furniture Factory Interface"""
-
- @abstractstaticmethod
- def get_furniture(furniture):
- """The static funiture factory inteface method"""
-
-
-class FurnitureFactory(IFurnitureFactory): # pylint: disable=too-few-public-methods
- """The Furniture Factory Concrete Class"""
-
- @staticmethod
- def get_furniture(furniture):
- """Static get_furniture method"""
- try:
- if furniture in ["SmallChair", "MediumChair", "BigChair"]:
- return ChairFactory().get_chair(furniture)
- if furniture in ["SmallTable", "MediumTable", "BigTable"]:
- return TableFactory().get_table(furniture)
- raise AssertionError("No Furniture Factory Found")
- except AssertionError as _e:
- print(_e)
- return None
-
-
-FURNITURE = FurnitureFactory.get_furniture("SmallChair")
-print(f"{FURNITURE.__class__} : {FURNITURE.dimensions()}")
-
-FURNITURE = FurnitureFactory.get_furniture("MediumTable")
-print(f"{FURNITURE.__class__} : {FURNITURE.dimensions()}")
-
-```
\ No newline at end of file
diff --git a/docs/abstract_factory.pdf b/docs/abstract_factory.pdf
deleted file mode 100644
index 09271ee..0000000
Binary files a/docs/abstract_factory.pdf and /dev/null differ
diff --git a/docs/abstract_factory.png b/docs/abstract_factory.png
deleted file mode 100644
index ba53bc3..0000000
Binary files a/docs/abstract_factory.png and /dev/null differ
diff --git a/docs/abstract_factory_furniture.png b/docs/abstract_factory_furniture.png
deleted file mode 100644
index 2ccfe0b..0000000
Binary files a/docs/abstract_factory_furniture.png and /dev/null differ
diff --git a/docs/adapter.md b/docs/adapter.md
deleted file mode 100644
index a5ffb1e..0000000
--- a/docs/adapter.md
+++ /dev/null
@@ -1,77 +0,0 @@
-# Adapter Design Pattern
-
-## Video Lecture
-Skillshare : https://skl.sh/34SM2Xg
-
-Udemy : Adapter Design Pattern
-
-## Description
-
-The adapter design pattern solves these problems:
-
-- How can a class be reused that does not have an interface that a client requires?
-- How can classes that have incompatible interfaces work together?
-- How can an alternative interface be provided for a class?
-
-In this lecture, I have 2 classes, they don't share the same interface. The client requires it's objects to use an already standardised interface.
-
-So we need to create an adapter, that wraps the incompatible object, but implements the standardised interface.
-
-## Two Incompatible Classes
-
-
-## After Creating an Adapter
-
-
-## Source Code
-
-### **`adapter.py`**
-```python
-from abc import ABCMeta, abstractmethod
-
-
-class IA(metaclass=ABCMeta):
- @staticmethod
- @abstractmethod
- def method_a():
- """An abstract method A"""
-
-
-class ClassA(IA):
- def method_a(self):
- print("method A")
-
-
-class IB(metaclass=ABCMeta):
- @staticmethod
- @abstractmethod
- def method_b():
- """An abstract method B"""
-
-
-class ClassB(IB):
- def method_b(self):
- print("method B")
-
-
-"""ClassB does not have a method_a, so we create an adapter"""
-
-
-class ClassBAdapter(IA):
- def __init__(self):
- self.class_b = ClassB()
-
- def method_a(self):
- """calls the class b method_b instead"""
- self.class_b.method_b()
-
-
-# client
-
-#ITEM = ClassA()
-#ITEM = ClassB() # has no method_a
-ITEM = ClassBAdapter()
-
-ITEM.method_a()
-
-```
\ No newline at end of file
diff --git a/docs/adapter.pdf b/docs/adapter.pdf
deleted file mode 100644
index 7e15199..0000000
Binary files a/docs/adapter.pdf and /dev/null differ
diff --git a/docs/adapter.png b/docs/adapter.png
deleted file mode 100644
index 762368e..0000000
Binary files a/docs/adapter.png and /dev/null differ
diff --git a/docs/adapter_before.png b/docs/adapter_before.png
deleted file mode 100644
index 6a2416c..0000000
Binary files a/docs/adapter_before.png and /dev/null differ
diff --git a/docs/atm.png b/docs/atm.png
deleted file mode 100644
index 7ffb880..0000000
Binary files a/docs/atm.png and /dev/null differ
diff --git a/docs/builder.md b/docs/builder.md
deleted file mode 100644
index 960ee2e..0000000
--- a/docs/builder.md
+++ /dev/null
@@ -1,150 +0,0 @@
-# Builder Design Pattern
-
-## Video Lecture
-
-Skillshare : [https://skl.sh/34SM2Xg](https://skl.sh/34SM2Xg "Builder Design Pattern")
-
-Udemy : [Builder Design Pattern](https://www.udemy.com/course/design-patterns-in-python/learn/lecture/16396852/?referralCode=7B677DD7A9580F2FFD8F "Builder Design Pattern")
-
-## Description
-
-The Builder Pattern is a creational pattern whose intent is to separate the construction of a complex object from its representation so that you can use the same construction process to create different representations.
-
-The Builder Pattern tries to solve,
-
-- How can a class create different representations of a complex object?
-- How can a class that includes creating a complex object be simplified?
-
-The Builder and Factory patterns are very similar in the fact they both instantiate new objects at run time. The difference is when the process of creating the object is more complex, so rather than the Factory returning a new instance of `ObjectA`, it could call the builders director construct method `ObjectA.construct()`. Both return an Object.
-
-Parts of the Builder Pattern
-
-- **Product** - The Product being built
-- **Concrete Builder** - Build the concrete product. Implements the IBuilder interface
-- **Builder Interface** - The Interface which the Concrete builder should implement
-- **Director** - Has a construct method which when called creates a customised product
-
-
-
-The Builder Pattern in the context of a House Builder. There are multiple directors creating there own complex objects
-
-.
-
-## Source Code
-
-### **`builder.py`**
-
-```python
-from abc import ABCMeta, abstractstaticmethod
-
-
-class IHouseBuilder(metaclass=ABCMeta):
- """The Builder Interface"""
-
- @abstractstaticmethod
- def set_wall_material(value):
- """Set the wall_material"""
-
- @abstractstaticmethod
- def set_building_type(value):
- """Set the building_type"""
-
- @abstractstaticmethod
- def set_number_doors(value):
- """Set the number of doors"""
-
- @abstractstaticmethod
- def set_number_windows(value):
- """Set the number of windows"""
-
- @abstractstaticmethod
- def get_result():
- """Return the house"""
-
-
-class HouseBuilder(IHouseBuilder):
- """The Concrete Builder."""
-
- def __init__(self):
- self.house = House()
-
- def set_wall_material(self, value):
- self.house.wall_material = value
- return self
-
- def set_building_type(self, value):
- self.house.building_type = value
- return self
-
- def set_number_doors(self, value):
- self.house.doors = value
- return self
-
- def set_number_windows(self, value):
- self.house.windows = value
- return self
-
- def get_result(self):
- return self.house
-
-
-class House():
- """The Product"""
-
- def __init__(self, building_type="Apartment", doors=0, windows=0, wall_material="Brick"):
- #brick, wood, straw, ice
- self.wall_material = wall_material
- # Apartment, Bungalow, Caravan, Hut, Castle, Duplex, HouseBoat, Igloo
- self.building_type = building_type
- self.doors = doors
- self.windows = windows
-
- def __str__(self):
- return "This is a {0} {1} with {2} door(s) and {3} window(s).".format(
- self.wall_material, self.building_type, self.doors, self.windows
- )
-
-
-class IglooDirector:
- """The Director, building a different representation."""
- @staticmethod
- def construct():
- return HouseBuilder()\
- .set_building_type("Igloo")\
- .set_wall_material("Ice")\
- .set_number_doors(1)\
- .set_number_windows(0)\
- .get_result()
-
-
-class HouseBoatDirector:
- """The Director, building a different representation."""
- @staticmethod
- def construct():
- return HouseBuilder()\
- .set_building_type("House Boat")\
- .set_wall_material("Wooden")\
- .set_number_doors(6)\
- .set_number_windows(8)\
- .get_result()
-
-
-class CastleDirector:
- """The Director, building a different representation."""
- @staticmethod
- def construct():
- return HouseBuilder()\
- .set_building_type("Castle")\
- .set_wall_material("Granite")\
- .set_number_doors(100)\
- .set_number_windows(200).get_result()
-
-
-if __name__ == "__main__":
- IGLOO = IglooDirector.construct()
- HOUSE_BOAT = HouseBoatDirector.construct()
- CASTLE = CastleDirector.construct()
- print(IGLOO)
- print(HOUSE_BOAT)
- print(CASTLE)
-```
diff --git a/docs/builder.pdf b/docs/builder.pdf
deleted file mode 100644
index c47f06e..0000000
Binary files a/docs/builder.pdf and /dev/null differ
diff --git a/docs/builder.png b/docs/builder.png
deleted file mode 100644
index cf194b1..0000000
Binary files a/docs/builder.png and /dev/null differ
diff --git a/docs/chain_of_responsibility.md b/docs/chain_of_responsibility.md
deleted file mode 100644
index f4ab783..0000000
--- a/docs/chain_of_responsibility.md
+++ /dev/null
@@ -1,176 +0,0 @@
-# Chain of Responsibility Design Pattern
-
-## Video Lecture
-Skillshare : https://skl.sh/34SM2Xg
-
-Udemy : Chain of Responsibility Design Pattern
-
-## Description
-
-Chain of responsibility pattern is a behavioural pattern used to achieve loose coupling
-in software design.
-In this example, a request from a client is passed to a chain of objects to process them.
-The objects in the chain will decide how to process them and/or pass them to the next in the chain.
-The objects can also modify the next in the chain if for example you wanted to run objects in a recursive manner.
-
-
-### Chain of Responsibility Diagram
-
-
-
-### Chain of Responsibility UML Diagram in the context of an ATM
-
-
-In the ATM example, the chain is created to dispense an amount of £50, then £20s and then £10s in order.
-The successor chain is hard coded in the chain client.
-
-```python
-def __init__(self):
- # initialize the successor chain
- self.chain1 = Dispenser50()
- self.chain2 = Dispenser20()
- self.chain3 = Dispenser10()
-
- # set the chain of responsibility
- # The Client may compose chains once or
- # the handler can set them dynamically at
- # handle time
- self.chain1.set_successor(self.chain2)
- self.chain2.set_successor(self.chain3)
-
-```
-You also have the option to set the next successor on logic at handle time.
-
-### Output
-```bash
-$ python atm.py
-Enter amount to withdrawal
-130
-Dispensing 2 £50 note
-Dispensing 1 £20 note
-Dispensing 1 £10 note
-Go spoil yourself
-```
-
-## Source Code
-
-### **`atm.py`**
-```python
-from abc import ABCMeta, abstractstaticmethod
-
-
-class IHandler(metaclass=ABCMeta):
- @abstractstaticmethod
- def set_successor(successor):
- """Set the next handler in the chain"""
-
- @abstractstaticmethod
- def handle(amount):
- """Handle the event"""
-
-
-class Dispenser50(IHandler):
- """ConcreteHandler
- Dispense £50 notes if applicable,
- otherwise continue to successor
- """
-
- def __init__(self):
- self._successor = None
-
- def set_successor(self, successor):
- """Set the successor"""
- self._successor = successor
-
- def handle(self, amount):
- """Handle the dispensing of notes"""
- if amount >= 50:
- num = amount // 50
- remainder = amount % 50
- print(f"Dispensing {num} £50 note")
- if remainder != 0:
- self._successor.handle(remainder)
- else:
- self._successor.handle(amount)
-
-
-class Dispenser20(IHandler):
- """ConcreteHandler
- Dispense £20 notes if applicable,
- otherwise continue to successor
- """
-
- def __init__(self):
- self._successor = None
-
- def set_successor(self, successor):
- """Set the successor"""
- self._successor = successor
-
- def handle(self, amount):
- """Handle the dispensing of notes"""
- if amount >= 20:
- num = amount // 20
- remainder = amount % 20
- print(f"Dispensing {num} £20 note")
- if remainder != 0:
- self._successor.handle(remainder)
- else:
- self._successor.handle(amount)
-
-
-class Dispenser10(IHandler):
- """ConcreteHandler
- Dispense £10 notes if applicable,
- otherwise continue to successor
- """
-
- def __init__(self):
- self._successor = None
-
- def set_successor(self, successor):
- """Set the successor"""
- self._successor = successor
-
- def handle(self, amount):
- """Handle the dispensing of notes"""
- if amount >= 10:
- num = amount // 10
- remainder = amount % 10
- print(f"Dispensing {num} £10 note")
- if remainder != 0:
- self._successor.handle(remainder)
- else:
- self._successor.handle(amount)
-
-
-class ATMDispenserChain: # pylint: disable=too-few-public-methods
- """The Chain Client"""
-
- def __init__(self):
- # initialize the successor chain
- self.chain1 = Dispenser50()
- self.chain2 = Dispenser20()
- self.chain3 = Dispenser10()
-
- # set the chain of responsibility
- # The Client may compose chains once or
- # the hadler can set them dynamically at
- # handle time
- self.chain1.set_successor(self.chain2)
- self.chain2.set_successor(self.chain3)
-
-
-if __name__ == "__main__":
-
- ATM = ATMDispenserChain()
-
- AMOUNT = int(input("Enter amount to withdrawal : "))
- if AMOUNT < 10 or AMOUNT % 10 != 0:
- print("Amount should be positive and in multiple of 10s.")
- exit()
- # process the request
- ATM.chain1.handle(AMOUNT)
- print("Now go spoil yourself")
-
-```
\ No newline at end of file
diff --git a/docs/chain_of_responsibility.pdf b/docs/chain_of_responsibility.pdf
deleted file mode 100644
index a32b2e9..0000000
Binary files a/docs/chain_of_responsibility.pdf and /dev/null differ
diff --git a/docs/chain_of_responsibility.png b/docs/chain_of_responsibility.png
deleted file mode 100644
index d6e01ed..0000000
Binary files a/docs/chain_of_responsibility.png and /dev/null differ
diff --git a/docs/command.md b/docs/command.md
deleted file mode 100644
index 3d730dc..0000000
--- a/docs/command.md
+++ /dev/null
@@ -1,136 +0,0 @@
-# Command Design Pattern
-
-## Video Lecture
-Skillshare : https://skl.sh/34SM2Xg
-
-Udemy : Command Design Pattern
-
-## Description
-
-The command pattern is a behavioural design pattern, in which an abstraction exists between an object that invokes a command, and the object that performs it.
-
-The components of the Command Design Pattern are,
-
-- **Receiver** : The Object that will receive and execute the command
-- **Invoker** : Which will send the command to the receiver
-- **Command Object** : Itself, which implements an execute, or action method, and contains all required information to execute it
-- **Client** : The application or component which is aware of the Receiver, Invoker and Commands
-
-Eg, a button, will call the Invoker, which will call a pre registered Commands execute method, which the Receiver will perform.
-
-A Concrete Class will delegate a request to a command object, instead of implementing the request directly.
-Using a command design pattern allows you to separate concerns a little easier and to solve problems of the concerns independently of each of the layers.
-eg, logging the execution of a command and it's outcome.
-
-Uses:
-
-- GUI Buttons, menus
-- Macro recording
-- Multi level undo/redo
-- Networking - send whole command objects across a network, even as a batch
-- Parallel processing or thread pools
-- Transactional behaviour
-- Wizards
-
-Notes:
-The receiver object should manages it's own state, not the command object
-There can be one or more invokers which can execute the command at a later date.
-
-
-
-The Command Pattern in the context of a light switch
-
-
-
-## Source Code
-### **`switch_command.py`**
-```python
-from abc import ABCMeta, abstractstaticmethod
-import time
-
-
-class ICommand(metaclass=ABCMeta):
- """The command interface, which all commands will implement"""
-
- @abstractstaticmethod
- def execute():
- """The required execute method which all command objects will use"""
-
-
-class Switch:
- """The Invoker Class"""
-
- def __init__(self):
- self._commands = {}
- self._history = []
-
- @property
- def history(self):
- return self._history
-
- def register(self, command_name, command):
- self._commands[command_name] = command
-
- def execute(self, command_name):
- if command_name in self._commands.keys():
- self._history.append((time.time(), command_name))
- self._commands[command_name].execute()
- else:
- print(f"Command [{command_name}] not recognised")
-
-
-class Light:
- """The Receiver"""
-
- def turn_on(self):
- print("Light turned ON")
-
- def turn_off(self):
- print("Light turned OFF")
-
-
-class SwitchOnCommand(ICommand):
- """A Command object, which implements the ICommand interface"""
-
- def __init__(self, light):
- self._light = light
-
- def execute(self):
- self._light.turn_on()
-
-
-class SwitchOffCommand(ICommand):
- """A Command object, which implements the ICommand interface"""
-
- def __init__(self, light):
- self._light = light
-
- def execute(self):
- self._light.turn_off()
-
-
-if __name__ == "__main__":
- # The Client is the main python app
-
- # The Light is the Receiver
- LIGHT = Light()
-
- # Create Commands
- SWITCH_ON = SwitchOnCommand(LIGHT)
- SWITCH_OFF = SwitchOffCommand(LIGHT)
-
- # Register the commands with the invoker (Switch)
- SWITCH = Switch()
- SWITCH.register("ON", SWITCH_ON)
- SWITCH.register("OFF", SWITCH_OFF)
-
- # Execute the commands that are registered on the Invoker
- SWITCH.execute("ON")
- SWITCH.execute("OFF")
- SWITCH.execute("ON")
- SWITCH.execute("OFF")
-
- # For fun, we can see the history
- print(SWITCH.history)
-
-```
diff --git a/docs/command.pdf b/docs/command.pdf
deleted file mode 100644
index b20da16..0000000
Binary files a/docs/command.pdf and /dev/null differ
diff --git a/docs/command_direct.png b/docs/command_direct.png
deleted file mode 100644
index 10f0aa0..0000000
Binary files a/docs/command_direct.png and /dev/null differ
diff --git a/docs/command_pattern.png b/docs/command_pattern.png
deleted file mode 100644
index 214d14c..0000000
Binary files a/docs/command_pattern.png and /dev/null differ
diff --git a/docs/command_undo_redo.md b/docs/command_undo_redo.md
deleted file mode 100644
index 41c5dec..0000000
--- a/docs/command_undo_redo.md
+++ /dev/null
@@ -1,193 +0,0 @@
-# Command Design Pattern (Undo/Redo)
-
-## Video Lecture
-Skillshare : https://skl.sh/34SM2Xg
-
-Udemy : Command (Undo/Redo) Design Pattern
-
-## Description
-
-This is part 2 of the Command Design Pattern tutorial, where I create a slider, instead of the switch from part 1.
-
-The slider also accepts a variable percentage, rather than an ON/OFF.
-
-The history also records the variable setting.
-
-And I also add UNDO/REDO to the Invoker so that you can go backwards and forwards through time.
-
-
-
-## Source Code
-### **`slider_command.py`**
-```python
-from abc import ABCMeta, abstractstaticmethod
-import time
-
-
-class ICommand(metaclass=ABCMeta):
- """The command interface, which all commands will implement"""
-
- @abstractstaticmethod
- def execute(*args):
- """The required execute method which all command obejcts will use"""
-
-
-class IUndoRedo(metaclass=ABCMeta):
- """The Undo Redo interface"""
- @abstractstaticmethod
- def history():
- """the history of the states"""
-
- @abstractstaticmethod
- def undo():
- """for undoing the hsitory of the states"""
-
- @abstractstaticmethod
- def redo():
- """for redoing the hsitory of the states"""
-
-
-class Slider(IUndoRedo):
- """The Invoker Class"""
-
- def __init__(self):
- self._commands = {}
- self._history = [(0.0, "OFF", ())] # A default setting of OFF
- self._history_position = 0 # The position that is used for UNDO/REDO
-
- @property
- def history(self):
- """Return all records in the History list"""
- return self._history
-
- def register(self, command_name, command):
- """All commands are registered in the Invoker Class"""
- self._commands[command_name] = command
-
- def execute(self, command_name, *args):
- """Execute a pre defined command and log in history"""
- if command_name in self._commands.keys():
- self._history_position += 1
- self._commands[command_name].execute(args)
- if len(self._history) == self._history_position:
- # This is a new event in hisory
- self._history.append((time.time(), command_name, args))
- else:
- # This occurs if there was one of more UNDOs and then a new
- # execute command happened. In case of UNDO, the history_position
- # changes, and executing new commands purges any history after
- # the current position"""
- self._history = self._history[:self._history_position+1]
- self._history[self._history_position] = {
- time.time(): [command_name, args]
- }
- else:
- print(f"Command [{command_name}] not recognised")
-
- def undo(self):
- """Undo a command if there is a command that can be undone.
- Update the history position so that further UNDOs or REDOs
- point to the correct index"""
- if self._history_position > 0:
- self._history_position -= 1
- self._commands[
- self._history[self._history_position][1]
- ].execute(self._history[self._history_position][2])
- else:
- print("nothing to undo")
-
- def redo(self):
- """Perform a REDO if the history_position is less than the end of the history list"""
- if self._history_position + 1 < len(self._history):
- self._history_position += 1
- self._commands[
- self._history[self._history_position][1]
- ].execute(self._history[self._history_position][2])
- else:
- print("nothing to REDO")
-
-
-class Heater:
- """The Receiver"""
-
- def set_to_max(self):
- print("Heater is ON and set to MAX (100%)")
-
- def set_to_percent(self, *args):
- print(f"Heater is ON and set to {args[0][0]}%")
-
- def turn_off(self):
- print("Heater is OFF")
-
-
-class SliderMaxCommand(ICommand):
- """A Command object, which implements the ICommand interface"""
-
- def __init__(self, heater):
- self._heater = heater
-
- def execute(self, *args):
- self._heater.set_to_max()
-
-
-class SliderPercentCommand(ICommand):
- """A Command object, which implements the ICommand interface"""
-
- def __init__(self, heater):
- self._heater = heater
-
- def execute(self, *args):
- self._heater.set_to_percent(args[0])
-
-
-class SliderOffCommand(ICommand):
- """A Command object, which implements the ICommand interface"""
-
- def __init__(self, heater):
- self._heater = heater
-
- def execute(self, *args):
- self._heater.turn_off()
-
-
-if __name__ == "__main__":
- # The Client is the main python app
-
- # The HEATER is the Receiver
- HEATER = Heater()
-
- # Create Commands
- SLIDER_MAX = SliderMaxCommand(HEATER)
- SLIDER_PERCENT = SliderPercentCommand(HEATER)
- SLIDER_OFF = SliderOffCommand(HEATER)
-
- # Register the commands with the invoker (Switch)
- SLIDER = Slider()
- SLIDER.register("MAX", SLIDER_MAX)
- SLIDER.register("PERCENT", SLIDER_PERCENT)
- SLIDER.register("OFF", SLIDER_OFF)
-
- # Execute the commands that are registered on the Invoker
- SLIDER.execute("PERCENT", 10)
- SLIDER.execute("PERCENT", 20)
- SLIDER.execute("PERCENT", 30)
- SLIDER.execute("PERCENT", 40)
- SLIDER.execute("PERCENT", 50)
- print(SLIDER.history)
- SLIDER.undo()
- SLIDER.undo()
- SLIDER.undo()
- SLIDER.redo()
- SLIDER.undo()
- SLIDER.undo()
- SLIDER.execute("PERCENT", 90)
- SLIDER.execute("MAX")
- SLIDER.execute("OFF")
- print(SLIDER.history)
- SLIDER.undo()
- SLIDER.redo()
-
- print(SLIDER.history)
-
-
-```
diff --git a/docs/command_undo_redo.pdf b/docs/command_undo_redo.pdf
deleted file mode 100644
index 3b6cc12..0000000
Binary files a/docs/command_undo_redo.pdf and /dev/null differ
diff --git a/docs/composite.md b/docs/composite.md
deleted file mode 100644
index b689565..0000000
--- a/docs/composite.md
+++ /dev/null
@@ -1,71 +0,0 @@
-# Composite Design Pattern
-
-## Video Lecture
-Skillshare : https://skl.sh/34SM2Xg
-
-Udemy : Composite Design Pattern
-
-## Description
-
-The Composite design pattern,
-- allows you to represent individual entities and groups of entities in the same manner.
-- is a structural design pattern that lets you compose objects into a tree.
-- is great if you need the option of swapping hierarchal relationships around.
-- makes it easier for you to add new kinds of components
-- provides flexibility of structure
-- conform to the Single Responsibility Principle in the way that it separates the aggregation of objects from the features of the object.
-
-Examples of using the Composite Design Pattern can be seen in a filesystem directory structure, where you can swap the hierarchy of folders, and in a drawing program where you can group, un-group and transform objects, and multiple objects at the same time.
-
-
-
-## Source Code
-
-### **`composite.py`**
-```python
-from abc import ABCMeta, abstractmethod
-
-class IGraphic(metaclass=ABCMeta):
- @staticmethod
- @abstractmethod
- def print():
- """print information"""
-
-class Ellipse(IGraphic):
- def print(self):
- print("Ellipse")
-
-class Circle(IGraphic):
- def print(self):
- print("Circle")
-
-class CompositeGraphic(IGraphic):
- def __init__(self):
- self.child_graphics = []
-
- def add(self, graphic):
- self.child_graphics.append(graphic)
-
- def print(self):
- for g in self.child_graphics:
- g.print()
-
-
-ELLIPSE1 = Ellipse()
-CIRCLE1 = Circle()
-
-COMPOSITE1 = CompositeGraphic()
-COMPOSITE1.add(ELLIPSE1)
-
-COMPOSITE2 = CompositeGraphic()
-COMPOSITE2.add(CIRCLE1)
-COMPOSITE2.add(COMPOSITE1)
-
-COMPOSITE2.print()
-
-# ELLIPSE1.print()
-# CIRCLE1.print()
-
-```
-
-
diff --git a/docs/composite.pdf b/docs/composite.pdf
deleted file mode 100644
index eb2ab0d..0000000
Binary files a/docs/composite.pdf and /dev/null differ
diff --git a/docs/composite.png b/docs/composite.png
deleted file mode 100644
index 7f3cd84..0000000
Binary files a/docs/composite.png and /dev/null differ
diff --git a/docs/decorator.md b/docs/decorator.md
deleted file mode 100644
index 9935f4d..0000000
--- a/docs/decorator.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# Decorator Design Pattern
-
-## Video Lecture
-Skillshare : https://skl.sh/34SM2Xg
-
-Udemy : Decorator Design Pattern
-
-## Description
-The decorator pattern is a structural pattern, that allows you to attach additional responsibilities to an object at run time.
-
-The decorator pattern is used in both the Object Oriented and Functional paradigms.
-
-The decorator pattern is different than the Python language feature of Python Decorators in it's syntax, but the application of it is the same, in the way that it is essentially a wrapper.
-
-The Decorator pattern adds extensibility, without modifying the original function.
-
-
-
-## Source Code
-
-### **`decorator.py`**
-```python
-class UndecoratedObject:
- @staticmethod
- def get():
- return "UndecoratedObject"
-
-
-class Decorate:
- def __init__(self, undecorated):
- self.undecorated = undecorated
-
- def get(self):
- return self.undecorated.get().replace("Undecorated", "Decorated")
-
-
-# class DecorateWithANewMethod:
-# def __init__(self, undecorated):
-# self.undecorated = undecorated
-
-# def get(self):
-# return self.undecorated.get()
-
-# def draw(self):
-# print(self.undecorated.get())
-
-
-UNDECORATED = UndecoratedObject()
-print(UNDECORATED.get())
-DECORATED = Decorate(UNDECORATED)
-print(DECORATED.get())
-#DECORATEDWITHNEWMETHOD = DecorateWithANewMethod(DECORATED)
-#DECORATEDWITHNEWMETHOD.draw()
-
-```
\ No newline at end of file
diff --git a/docs/decorator.pdf b/docs/decorator.pdf
deleted file mode 100644
index b8bd37a..0000000
Binary files a/docs/decorator.pdf and /dev/null differ
diff --git a/docs/decorator.png b/docs/decorator.png
deleted file mode 100644
index 3be9c01..0000000
Binary files a/docs/decorator.png and /dev/null differ
diff --git a/docs/facade.md b/docs/facade.md
deleted file mode 100644
index 3d155a5..0000000
--- a/docs/facade.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# Facade Design Pattern
-
-## Video Lecture
-Skillshare : https://skl.sh/34SM2Xg
-
-Udemy : Facade Design Pattern
-
-## Description
-
-The Facade Pattern is a structural design pattern.
-It provides a simplified interface to a set of other interfaces, abstractions and implementations within a system that may be full of complexity and/or tightly coupled.
-
-
-
-## Source Code
-
-### **`facade.py`**
-```python
-class SubSystemClassA:
- @staticmethod
- def method():
- return "A"
-
-
-class SubSystemClassB:
- @staticmethod
- def method():
- return "B"
-
-
-class SubSystemClassC:
- @staticmethod
- def method():
- return "C"
-
-
-# facade
-class Facade:
- def __init__(self):
- self.sub_system_class_a = SubSystemClassA()
- self.sub_system_class_b = SubSystemClassB()
- self.sub_system_class_c = SubSystemClassC()
-
- def create(self):
- result = self.sub_system_class_a.method()
- result += self.sub_system_class_b.method()
- result += self.sub_system_class_c.method()
- return result
-
-
-# client
-FACADE = Facade()
-RESULT = FACADE.create()
-print("The Result = %s" % RESULT)
-
-```
\ No newline at end of file
diff --git a/docs/facade.pdf b/docs/facade.pdf
deleted file mode 100644
index de3f978..0000000
Binary files a/docs/facade.pdf and /dev/null differ
diff --git a/docs/facade.png b/docs/facade.png
deleted file mode 100644
index 94352b1..0000000
Binary files a/docs/facade.png and /dev/null differ
diff --git a/docs/factory.md b/docs/factory.md
deleted file mode 100644
index b29a9be..0000000
--- a/docs/factory.md
+++ /dev/null
@@ -1,93 +0,0 @@
-# Factory Design Pattern
-
-## Video Lecture
-Skillshare : https://skl.sh/34SM2Xg
-
-Udemy : Factory Design Pattern
-
-
-## Description
-
-The Factory Pattern is a creational pattern that defines an Interface for creating an object and defers instantiation until runtime.
-
-Used when you don't know how many or what type of objects will be needed until or during runtime
-
-
-
-The Factory Pattern in the context of a Chair Factory 
-
-## Source Code
-
-### **`chair_factory.py`**
-
-```python
-from abc import ABCMeta, abstractstaticmethod
-
-
-class IChair(metaclass=ABCMeta): # pylint: disable=too-few-public-methods
- """The Chair Interface"""
-
- @abstractstaticmethod
- def dimensions():
- """A static inteface method"""
-
-
-class BigChair(IChair): # pylint: disable=too-few-public-methods
- """The Big Chair Concrete Class which implements the IChair interface"""
-
- def __init__(self):
- self._height = 80
- self._width = 80
- self._depth = 80
-
- def dimensions(self):
- return {"width": self._width, "depth": self._depth, "height": self._height}
-
-
-class MediumChair(IChair): # pylint: disable=too-few-public-methods
- """The Medium Chair Concrete Class which implements the IChair interface"""
-
- def __init__(self):
- self._height = 60
- self._width = 60
- self._depth = 60
-
- def dimensions(self):
- return {"width": self._width, "depth": self._depth, "height": self._height}
-
-
-class SmallChair(IChair): # pylint: disable=too-few-public-methods
- """The Small Chair Concrete Class which implements the IChair interface"""
-
- def __init__(self):
- self._height = 40
- self._width = 40
- self._depth = 40
-
- def dimensions(self):
- return {"width": self._width, "depth": self._depth, "height": self._height}
-
-
-class ChairFactory: # pylint: disable=too-few-public-methods
- """Tha Factory Class"""
-
- @staticmethod
- def get_chair(chair):
- """A static method to get a table"""
- try:
- if chair == "BigChair":
- return BigChair()
- if chair == "MediumChair":
- return MediumChair()
- if chair == "SmallChair":
- return SmallChair()
- raise AssertionError("Chair Not Found")
- except AssertionError as _e:
- print(_e)
- return None
-
-
-if __name__ == "__main__":
- CHAIR_FACTORY = ChairFactory().get_chair("SmallChair")
- print(CHAIR_FACTORY.dimensions())
-```
diff --git a/docs/factory.pdf b/docs/factory.pdf
deleted file mode 100644
index 563ac5a..0000000
Binary files a/docs/factory.pdf and /dev/null differ
diff --git a/docs/factory.png b/docs/factory.png
deleted file mode 100644
index 90cc146..0000000
Binary files a/docs/factory.png and /dev/null differ
diff --git a/docs/factory_pattern.png b/docs/factory_pattern.png
deleted file mode 100644
index cc01bdb..0000000
Binary files a/docs/factory_pattern.png and /dev/null differ
diff --git a/docs/factory_pattern_chair.png b/docs/factory_pattern_chair.png
deleted file mode 100644
index 98ac417..0000000
Binary files a/docs/factory_pattern_chair.png and /dev/null differ
diff --git a/docs/house_builder.png b/docs/house_builder.png
deleted file mode 100644
index d672560..0000000
Binary files a/docs/house_builder.png and /dev/null differ
diff --git a/docs/img/skillshare_btn.png b/docs/img/skillshare_btn.png
deleted file mode 100644
index c7a65ef..0000000
Binary files a/docs/img/skillshare_btn.png and /dev/null differ
diff --git a/docs/img/udemy_btn.png b/docs/img/udemy_btn.png
deleted file mode 100644
index 68c7c24..0000000
Binary files a/docs/img/udemy_btn.png and /dev/null differ
diff --git a/docs/index.md b/docs/index.md
deleted file mode 100644
index a629a35..0000000
--- a/docs/index.md
+++ /dev/null
@@ -1,59 +0,0 @@
-# Design Patterns In Python
-
-The tutorials in this documentation supplement my **Design Patterns in Python** Courses on Skillshare and
-Udemy.
-
-To register for this course visit
-
-
+Facade Use Case |
+Python **Decimal** |
+Python Type Hints |
+
+## Book
+
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
+
+## Overview
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Facade UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./facade/facade_concept.py
+A
+B
+{'C': [1, 2, 3]}
+A
+B
+{'C': [1, 2, 3]}
+```
+
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./facade/client.py
+
+---- Gamestate Snapshot ----
+{'clock': 59, 'game_open': True, 'entries': [('sean', Decimal('5'))]}
+
+---- Reports History ----
+0 : 1614087127.327007 : new user `sean` created
+1 : 1614087127.327007 : wallet for `sean` created and set to 0
+2 : 1614087127.327007 : Give new user `sean` sign up bonus of 10
+3 : 1614087127.327007 : Balance adjustment for `sean`. New balance = 10
+4 : 1614087128.3278701 : Balance check for `sean` = 10
+5 : 1614087128.3278701 : Balance adjustment for `sean`. New balance = 9
+6 : 1614087128.3278701 : New entry `5` submitted by `sean`
+
+---- Gamestate Snapshot ----
+{'clock': 58, 'game_open': True, 'entries': [('sean', Decimal('5'))]}
+```
+
+## New Coding Concepts
+
+### Python `decimal` Module
+
+The `decimal` module provides support for correctly rounded decimal floating-point arithmetic.
+
+If representing money values in python, it is better to use the `decimal` type rather than `float` .
+
+Floats will have rounding errors versus decimal.
+
+``` python
+from decimal import Decimal
+
+print(1.1 + 2.2) # adding floats
+print(Decimal('1.1') + Decimal('2.2')) # adding decimals
+```
+
+Outputs
+
+```
+
+3.3000000000000003
+3.3
+```
+
+Note how the float addition results in `3.3000000000000003` whereas the decimal addition result equals `3.3` .
+
+Be aware though that when creating decimals, be sure to pass in a string representation, otherwise it will create a decimal from a float.
+
+``` python
+from decimal import *
+
+print(Decimal(1.1)) # decimal from float
+print(Decimal('1.1')) # decimal from string
+```
+
+Outputs
+
+```
+
+1.100000000000000088817841970012523233890533447265625
+1.1
+```
+
+Python Decimal: [https://docs.python.org/3/library/decimal.html](https://docs.python.org/3/library/decimal.html)
+
+### Type Hints
+
+In the Facade use case example, I have added type hints to the method signatures and class attributes.
+
+``` python
+ _clock: int = 0
+ _entries: list[tuple[str, Decimal]] = []
+
+ ...
+
+ def get_balance(user_id: str) -> Decimal:
+ "Get a players balance"
+ ...
+
+ ...
+
+ def register_user(cls, new_user: dict[str, str]) -> str:
+ "register a user"
+ ...
+
+```
+
+See the extra `: str` after the `user_id` attribute, and the `-> Decimal` before the final colon in the `get_balance()` snippet.
+
+This is indicating that if you use the `get_balance()` method, that the `user_id` should be a type of `string`, and that the method will return a `Decimal` .
+
+Note that the Python runtime does not enforce the type hints and that they are optional. However, where they are beneficial is in the IDE of your choice or other third party tools such type checkers.
+
+In VSCode, when typing code, it will show the types that the method needs.
+
+
+
+For type checking, you can install an extra module called `mypy`
+
+``` bash
+pip install mypy
+```
+
+and then run it against your code,
+
+``` bash
+mypy ./facade/client.py
+Success: no issues found in 1 source file
+```
+
+Mypy will also check any imported modules at the same time.
+
+If working with money, then it is advisable to add extra checks to your code. Checking that type usage is consistent throughout your code, especially when using Decimals, is a good idea that will make your code more robust.
+
+For example, if I wasn't consistent in using the Decimal throughout my code, then I would see a warning highlighted.
+
+``` bash
+mypy ./facade/client.py
+facade/game_engine.py:45: error: Argument 1 to "append" of "list" has incompatible type "Tuple[str, int]"; expected "Tuple[str, Decimal]"
+facade/game_api.py:34: error: Argument 2 to "submit_entry" of "GameEngine" has incompatible type "Decimal"; expected "int"
+Found 2 errors in 2 files (checked 1 source file)
+```
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/facade/car_facade.dot b/facade/car_facade.dot
deleted file mode 100644
index 6a426c9..0000000
--- a/facade/car_facade.dot
+++ /dev/null
@@ -1,11 +0,0 @@
-digraph "classes" {
-charset="utf-8"
-rankdir=BT
-"0" [label="{CarBody|\l|SetBody()\l}", shape="record"];
-"1" [label="{CarEngine|\l|SetEngine()\l}", shape="record"];
-"2" [label="{CarFacade|carBody\lcarEngine\lcarModel\l|CreateCompleteCar()\l}", shape="record"];
-"3" [label="{CarModel|\l|SetModel()\l}", shape="record"];
-"0" -> "2" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="carBody", style="solid"];
-"1" -> "2" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="carEngine", style="solid"];
-"3" -> "2" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="carModel", style="solid"];
-}
diff --git a/facade/classes.dot b/facade/classes.dot
deleted file mode 100644
index c6a5cdd..0000000
--- a/facade/classes.dot
+++ /dev/null
@@ -1,11 +0,0 @@
-digraph "classes" {
-charset="utf-8"
-rankdir=BT
-"0" [label="{Discount|\l|calc()\l}", shape="record"];
-"1" [label="{Fees|\l|calc()\l}", shape="record"];
-"2" [label="{Shipping|\l|calc()\l}", shape="record"];
-"3" [label="{ShopFacade|discount\lfees\lshipping\l|calc_price()\l}", shape="record"];
-"0" -> "3" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="discount", style="solid"];
-"1" -> "3" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="fees", style="solid"];
-"2" -> "3" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="shipping", style="solid"];
-}
diff --git a/facade/client.py b/facade/client.py
new file mode 100644
index 0000000..7c10fbc
--- /dev/null
+++ b/facade/client.py
@@ -0,0 +1,30 @@
+"The Facade Example Use Case"
+import time
+from decimal import Decimal
+from game_api import GameAPI
+
+USER = {"user_name": "sean"}
+USER_ID = GameAPI.register_user(USER)
+
+time.sleep(1)
+
+GameAPI.submit_entry(USER_ID, Decimal('5'))
+
+time.sleep(1)
+
+print()
+print("---- Gamestate Snapshot ----")
+print(GameAPI.game_state())
+
+time.sleep(1)
+
+HISTORY = GameAPI.get_history()
+
+print()
+print("---- Reports History ----")
+for row in HISTORY:
+ print(f"{row} : {HISTORY[row][0]} : {HISTORY[row][1]}")
+
+print()
+print("---- Gamestate Snapshot ----")
+print(GameAPI.game_state())
diff --git a/facade/facade.dot b/facade/facade.dot
deleted file mode 100644
index 3e28a35..0000000
--- a/facade/facade.dot
+++ /dev/null
@@ -1,11 +0,0 @@
-digraph "classes" {
-charset="utf-8"
-rankdir=BT
-"0" [label="{Facade|sub_system_class_a\lsub_system_class_b\lsub_system_class_c\l|create()\l}", shape="record"];
-"1" [label="{SubSystemClassA|\l|method()\l}", shape="record"];
-"2" [label="{SubSystemClassB|\l|method()\l}", shape="record"];
-"3" [label="{SubSystemClassC|\l|method()\l}", shape="record"];
-"1" -> "0" [arrowhead="diamond", arrowtail="none"];
-"2" -> "0" [arrowhead="diamond", arrowtail="none"];
-"3" -> "0" [arrowhead="diamond", arrowtail="none"];
-}
diff --git a/facade/facade.png b/facade/facade.png
deleted file mode 100644
index 94352b1..0000000
Binary files a/facade/facade.png and /dev/null differ
diff --git a/facade/facade.py b/facade/facade.py
deleted file mode 100644
index fec302b..0000000
--- a/facade/facade.py
+++ /dev/null
@@ -1,41 +0,0 @@
-"""
-Facade Design Pattern
-"""
-
-
-class SubSystemClassA:
- @staticmethod
- def method():
- return "A"
-
-
-class SubSystemClassB:
- @staticmethod
- def method():
- return "B"
-
-
-class SubSystemClassC:
- @staticmethod
- def method():
- return "C"
-
-
-# facade
-class Facade:
- def __init__(self):
- self.sub_system_class_a = SubSystemClassA()
- self.sub_system_class_b = SubSystemClassB()
- self.sub_system_class_c = SubSystemClassC()
-
- def create(self):
- result = self.sub_system_class_a.method()
- result += self.sub_system_class_b.method()
- result += self.sub_system_class_c.method()
- return result
-
-
-# client
-FACADE = Facade()
-RESULT = FACADE.create()
-print("The Result = %s" % RESULT)
diff --git a/facade/facade_concept.py b/facade/facade_concept.py
new file mode 100644
index 0000000..7d12f55
--- /dev/null
+++ b/facade/facade_concept.py
@@ -0,0 +1,56 @@
+# pylint: disable=too-few-public-methods
+"The Facade pattern concept"
+
+
+class SubSystemClassA:
+ "A hypothetically complicated class"
+ @staticmethod
+ def method():
+ "A hypothetically complicated method"
+ return "A"
+
+
+class SubSystemClassB:
+ "A hypothetically complicated class"
+ @staticmethod
+ def method(value):
+ "A hypothetically complicated method"
+ return value
+
+
+class SubSystemClassC:
+ "A hypothetically complicated class"
+ @staticmethod
+ def method(value):
+ "A hypothetically complicated method"
+ return value
+
+
+class Facade():
+ "A simplified facade offering the services of subsystems"
+ @staticmethod
+ def sub_system_class_a():
+ "Use the subsystems method"
+ return SubSystemClassA().method()
+
+ @staticmethod
+ def sub_system_class_b(value):
+ "Use the subsystems method"
+ return SubSystemClassB().method(value)
+
+ @staticmethod
+ def sub_system_class_c(value):
+ "Use the subsystems method"
+ return SubSystemClassC().method(value)
+
+
+# The Client
+# call potentially complicated subsystems directly
+print(SubSystemClassA.method())
+print(SubSystemClassB.method("B"))
+print(SubSystemClassC.method({"C": [1, 2, 3]}))
+
+# or use the simplified facade
+print(Facade().sub_system_class_a())
+print(Facade().sub_system_class_b("B"))
+print(Facade().sub_system_class_c({"C": [1, 2, 3]}))
diff --git a/facade/game_api.py b/facade/game_api.py
new file mode 100644
index 0000000..32e6159
--- /dev/null
+++ b/facade/game_api.py
@@ -0,0 +1,40 @@
+"The Game API facade"
+from decimal import Decimal
+from users import Users
+from wallets import Wallets
+from game_engine import GameEngine
+from reports import Reports
+
+
+class GameAPI():
+ "The Game API facade"
+ @staticmethod
+ def get_balance(user_id: str) -> Decimal:
+ "Get a players balance"
+ return Wallets.get_balance(user_id)
+
+ @staticmethod
+ def game_state() -> dict:
+ "Get the current game state"
+ return GameEngine().get_game_state()
+
+ @staticmethod
+ def get_history() -> dict:
+ "get the game history"
+ return Reports.get_history()
+
+ @staticmethod
+ def change_pwd(user_id: str, password: str) -> bool:
+ "change users password"
+ return Users.change_pwd(user_id, password)
+
+ @staticmethod
+ def submit_entry(user_id: str, entry: Decimal) -> bool:
+ "submit a bet"
+ return GameEngine().submit_entry(user_id, entry)
+
+ @staticmethod
+ def register_user(value: dict[str, str]) -> str: # Python 3.9
+ # def register_user(value) -> str: # Python 3.8 and earlier
+ "register a new user and returns the new id"
+ return Users.register_user(value)
diff --git a/facade/game_engine.py b/facade/game_engine.py
new file mode 100644
index 0000000..3dd6ecd
--- /dev/null
+++ b/facade/game_engine.py
@@ -0,0 +1,56 @@
+"The Game Engine"
+import time
+from decimal import Decimal
+from wallets import Wallets
+from reports import Reports
+
+
+class GameEngine():
+ "The Game Engine"
+ _instance = None
+ _start_time: int = 0
+ _clock: int = 0
+ _entries: list[tuple[str, Decimal]] = [] # Python 3.9
+ # _entries = [] # Python 3.8 or earlier
+ _game_open = True
+
+ def __new__(cls):
+ if cls._instance is None:
+ cls._instance = GameEngine
+ cls._start_time = int(time.time())
+ cls._clock = 60
+ return cls._instance
+
+ @classmethod
+ def get_game_state(cls) -> dict:
+ "Get a snapshot of the current game state"
+ now = int(time.time())
+ time_remaining = cls._start_time - now + cls._clock
+ if time_remaining < 0:
+ time_remaining = 0
+ cls._game_open = False
+ return {
+ "clock": time_remaining,
+ "game_open": cls._game_open,
+ "entries": cls._entries
+ }
+
+ @classmethod
+ def submit_entry(cls, user_id: str, entry: Decimal) -> bool:
+ "Submit a new entry for the user in this game"
+ now = int(time.time())
+ time_remaining = cls._start_time - now + cls._clock
+ if time_remaining > 0:
+ if Wallets.get_balance(user_id) > Decimal('1'):
+ if Wallets.adjust_balance(user_id, Decimal('-1')):
+ cls._entries.append((user_id, entry))
+ Reports.log_event(
+ f"New entry `{entry}` submitted by `{user_id}`")
+ return True
+ Reports.log_event(
+ f"Problem adjusting balance for `{user_id}`")
+ return False
+ Reports.log_event(f"User Balance for `{user_id}` to low")
+ return False
+ Reports.log_event("Game Closed")
+ return False
diff --git a/facade/reports.py b/facade/reports.py
new file mode 100644
index 0000000..0f86456
--- /dev/null
+++ b/facade/reports.py
@@ -0,0 +1,24 @@
+"A Singleton Dictionary of Reported Events"
+import time
+
+
+class Reports():
+ "A Singleton Dictionary of Reported Events"
+ _reports: dict[int, tuple[float, str]] = {} # Python 3.9
+ # _reports = {} # Python 3.8 or earlier
+ _row_id = 0
+
+ def __new__(cls):
+ return cls
+
+ @classmethod
+ def get_history(cls) -> dict:
+ "A method to retrieve all historic events"
+ return cls._reports
+
+ @classmethod
+ def log_event(cls, event: str) -> bool:
+ "A method to add a new event to the record"
+ cls._reports[cls._row_id] = (time.time(), event)
+ cls._row_id = cls._row_id + 1
+ return True
diff --git a/facade/shop_facade.dot b/facade/shop_facade.dot
deleted file mode 100644
index c6a5cdd..0000000
--- a/facade/shop_facade.dot
+++ /dev/null
@@ -1,11 +0,0 @@
-digraph "classes" {
-charset="utf-8"
-rankdir=BT
-"0" [label="{Discount|\l|calc()\l}", shape="record"];
-"1" [label="{Fees|\l|calc()\l}", shape="record"];
-"2" [label="{Shipping|\l|calc()\l}", shape="record"];
-"3" [label="{ShopFacade|discount\lfees\lshipping\l|calc_price()\l}", shape="record"];
-"0" -> "3" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="discount", style="solid"];
-"1" -> "3" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="fees", style="solid"];
-"2" -> "3" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="shipping", style="solid"];
-}
diff --git a/facade/shop_facade.png b/facade/shop_facade.png
deleted file mode 100644
index b328f78..0000000
Binary files a/facade/shop_facade.png and /dev/null differ
diff --git a/facade/shop_facade.py b/facade/shop_facade.py
deleted file mode 100644
index b782fb1..0000000
--- a/facade/shop_facade.py
+++ /dev/null
@@ -1,40 +0,0 @@
-"""A Facade Demo"""
-
-
-class Discount:
- @staticmethod
- def calc(value):
- return value * 0.9
-
-
-class Shipping:
- @staticmethod
- def calc():
- return 5
-
-
-class Fees:
- @staticmethod
- def calc(value):
- return value * 1.5
-
-
-# facade
-class ShopFacade:
- def __init__(self):
- self.discount = Discount()
- self.shipping = Shipping()
- self.fees = Fees()
-
- def calc_price(self, price):
- price = self.discount.calc(price)
- price = self.fees.calc(price)
- price += self.shipping.calc()
- return price
-
-
-# client
-SHOPFACADE = ShopFacade()
-BUYPRICE = 100
-COST = SHOPFACADE.calc_price(BUYPRICE)
-print("The Sell Cost = %.2f" % COST)
diff --git a/facade/users.py b/facade/users.py
new file mode 100644
index 0000000..f199bee
--- /dev/null
+++ b/facade/users.py
@@ -0,0 +1,46 @@
+"A Singleton Dictionary of Users"
+from decimal import Decimal
+from wallets import Wallets
+from reports import Reports
+
+
+class Users():
+ "A Singleton Dictionary of Users"
+ _users: dict[str, dict[str, str]] = {} # Python 3.9
+ # _users = {} # Python 3.8 or earlier
+
+ def __new__(cls):
+ return cls
+
+ @classmethod
+ def register_user(cls, new_user: dict[str, str]) -> str: # Python 3.9
+ # def register_user(cls, new_user) -> str: # Python 3.8 or earlier
+ "register a user"
+ if not new_user["user_name"] in cls._users:
+ # generate really complicated unique user_id.
+ # Using the existing user_name as the id for simplicity
+ user_id = new_user["user_name"]
+ cls._users[user_id] = new_user
+ Reports.log_event(f"new user `{user_id}` created")
+ # create a wallet for the new user
+ Wallets().create_wallet(user_id)
+ # give the user a sign up bonus
+ Reports.log_event(
+ f"Give new user `{user_id}` sign up bonus of 10")
+ Wallets().adjust_balance(user_id, Decimal(10))
+ return user_id
+ return ""
+
+ @classmethod
+ def edit_user(cls, user_id: str, user: dict):
+ "do nothing"
+ print(user_id)
+ print(user)
+ return False
+
+ @classmethod
+ def change_pwd(cls, user_id: str, password: str):
+ "do nothing"
+ print(user_id)
+ print(password)
+ return False
diff --git a/facade/wallets.py b/facade/wallets.py
new file mode 100644
index 0000000..996c610
--- /dev/null
+++ b/facade/wallets.py
@@ -0,0 +1,39 @@
+"A Singleton Dictionary of User Wallets"
+from decimal import Decimal
+from reports import Reports
+
+
+class Wallets():
+ "A Singleton Dictionary of User Wallets"
+ _wallets: dict[str, Decimal] = {} # Python 3.9
+ # _wallets = {} # Python 3.8 or earlier
+
+ def __new__(cls):
+ return cls
+
+ @classmethod
+ def create_wallet(cls, user_id: str) -> bool:
+ "A method to initialize a users wallet"
+ if not user_id in cls._wallets:
+ cls._wallets[user_id] = Decimal('0')
+ Reports.log_event(
+ f"wallet for `{user_id}` created and set to 0")
+ return True
+ return False
+
+ @classmethod
+ def get_balance(cls, user_id: str) -> Decimal:
+ "A method to check a users balance"
+ Reports.log_event(
+ f"Balance check for `{user_id}` = {cls._wallets[user_id]}")
+ return cls._wallets[user_id]
+
+ @classmethod
+ def adjust_balance(cls, user_id: str, amount: Decimal) -> Decimal:
+ "A method to adjust a user balance up or down"
+ cls._wallets[user_id] = cls._wallets[user_id] + Decimal(amount)
+ Reports.log_event(
+ f"Balance adjustment for `{user_id}`. "
+ f"New balance = {cls._wallets[user_id]}"
+ )
+ return cls._wallets[user_id]
diff --git a/factory/Factory Design Pattern.md b/factory/Factory Design Pattern.md
deleted file mode 100644
index d5a7802..0000000
--- a/factory/Factory Design Pattern.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Factory Design Pattern
-
-The Factory Pattern is a creational pattern that defines an Interface for creating an object and defers instantiation until runtime.
-
-Used when you don't know how many or what type of objects will be needed until or during runtime
-
-
-
-The Factory Pattern in the context of a Chair Factory
-
-
-```python
-class ObjectFactory:
- """Tha Factory Class"""
-
- @staticmethod
- def get_concrete_object(object_type):
- """A static method to get a concrete object of type class"""
- try:
- if object_type == "ObjectA":
- return ObjectA()
- if object_type == "ObjectB":
- return ObjectB()
- if object_type == "ObjectC":
- return ObjectC()
- raise AssertionError("Object Not Found")
- except AssertionError as _e:
- print(_e)
- return None
-```
-
-Each Object implements a Common Interface
-```python
-class ObjectA(IObjectType):
- """The Object Concrete Class which implements the IObjectType interface"""
-
- ...
-```
-
-Request from the factory at run time
-```python
-if __name__ == "__main__":
- MY_OBJECT = ObjectFactory().get_concrete_object("ObjectB")
- print(MY_OBJECT.dimensions())
-```
-
diff --git a/factory/Factory Design Pattern.pdf b/factory/Factory Design Pattern.pdf
deleted file mode 100644
index 0086754..0000000
Binary files a/factory/Factory Design Pattern.pdf and /dev/null differ
diff --git a/factory/README.md b/factory/README.md
index bcc23fa..4a6f2b9 100644
--- a/factory/README.md
+++ b/factory/README.md
@@ -1,45 +1,96 @@
# Factory Design Pattern
-The Factory Pattern is a creational pattern that defines an Interface for creating an object and defers instantiation until runtime.
-
-Used when you don't know how many or what type of objects will be needed until or during runtime
-
-
-
-The Factory Pattern in the context of a Chair Factory
-
-
-```python
-class ObjectFactory:
- """Tha Factory Class"""
-
- @staticmethod
- def get_concrete_object(object_type):
- """A static method to get a concrete object of type class"""
- try:
- if object_type == "ObjectA":
- return ObjectA()
- if object_type == "ObjectB":
- return ObjectB()
- if object_type == "ObjectC":
- return ObjectC()
- raise AssertionError("Object Not Found")
- except AssertionError as _e:
- print(_e)
- return None
+## Videos
+
+Section | Video Links
+-|-
+Factory Overview |
+Factory Use Case |
+**ABCMeta** Module |
+
+## Book
+
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
+
+## Overview
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Factory UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Output
+
+``` bash
+python ./factory/factory_concept.py
+ConcreteProductB
```
-Each Object implements a Common Interface
-```python
-class ObjectA(IObjectType):
- """The Object Concrete Class which implements the IObjectType interface"""
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Factory Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./factory/client.py
+{'width': 40, 'depth': 40, 'height': 40}
- ...
```
-Request from the factory at run time
-```python
-if __name__ == "__main__":
- MY_OBJECT = ObjectFactory().get_concrete_object("ObjectB")
- print(MY_OBJECT.dimensions())
+## New Coding Concepts
+
+### ABCMeta
+
+ABCMeta classes are a development tool that help you to write classes that conform to a specified interface that you've designed.
+
+ABCMeta refers to **A**bstract **B**ase **C**lasses.
+
+The benefits of using ABCMeta classes to create abstract classes is that your IDE and Pylint will indicate to you at development time whether your inheriting classes conform to the class definition that you've asked them to.
+
+Abstract classes are not instantiated directly in your scripts, but instead inherited by subclasses that will provide the implementation code for the abstract methods. E.g., you don't create `IChair`, but you create `SmallChair` that implemented the methods described in the `IChair` interface.
+
+An abstract method is a method that is declared, but contains no implementation. The implementation happens at the class that inherits the abstract class.
+
+You don't need to use ABCMeta classes and interfaces that you have created in your final python code. You code will still work without them.
+
+You can try it by removing the interfaces from all of the chair classes above, and you will see that your python program will still run.
+
+eg, change
+
+``` python
+class BigChair(IChair):
```
+
+to
+
+``` python
+class BigChair():
+```
+
+and it will still work.
+
+While it is possible to ensure your classes are correct without using abstract classes, it is often easier to use abstract classes as a backup method of checking correctness, especially if your projects become very large and involve many developers.
+
+Note that in all my code examples, the abstract classes are prefixed with a capital **I**, to indicate that they are abstract interfaces. They have no code in their methods. They do not require a `self` or `cls` argument due to the use of `@staticmethod` . The inheriting class will implement the code in each of the methods that the abstract class is describing. If subclasses are inheriting an abstract base class, and they do not implement the methods as described, there will be [Pylint error or warning message (E0110)](/coding-conventions.md#common-pylint-warning-and-error-messages).
+
+See PEP 3119 : [https://www.python.org/dev/peps/pep-3119/](https://www.python.org/dev/peps/pep-3119/)
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/factory/big_chair.py b/factory/big_chair.py
new file mode 100644
index 0000000..9345d91
--- /dev/null
+++ b/factory/big_chair.py
@@ -0,0 +1,19 @@
+# pylint: disable=too-few-public-methods
+"A Class of Chair"
+from interface_chair import IChair
+
+
+class BigChair(IChair):
+ "The Big Chair Concrete Class implements the IChair interface"
+
+ def __init__(self):
+ self._height = 80
+ self._width = 80
+ self._depth = 80
+
+ def get_dimensions(self):
+ return {
+ "width": self._width,
+ "depth": self._depth,
+ "height": self._height
+ }
diff --git a/factory/chair_factory.py b/factory/chair_factory.py
index 2dc1b3d..68bb14f 100644
--- a/factory/chair_factory.py
+++ b/factory/chair_factory.py
@@ -1,75 +1,19 @@
-"""A Factory Pattern Example
-The Factory Pattern is a creational pattern that defines an Interface for creating an object
-and defers instantiation until runtime.
-Used when you don't know how many or what type of objects will be needed until during runtime
-"""
-
-from abc import ABCMeta, abstractstaticmethod
-
-
-class IChair(metaclass=ABCMeta): # pylint: disable=too-few-public-methods
- """The Chair Interface"""
-
- @abstractstaticmethod
- def dimensions():
- """A static inteface method"""
-
-
-class BigChair(IChair): # pylint: disable=too-few-public-methods
- """The Big Chair Concrete Class which implements the IChair interface"""
-
- def __init__(self):
- self._height = 80
- self._width = 80
- self._depth = 80
-
- def dimensions(self):
- return {"width": self._width, "depth": self._depth, "height": self._height}
-
-
-class MediumChair(IChair): # pylint: disable=too-few-public-methods
- """The Medium Chair Concrete Class which implements the IChair interface"""
-
- def __init__(self):
- self._height = 60
- self._width = 60
- self._depth = 60
-
- def dimensions(self):
- return {"width": self._width, "depth": self._depth, "height": self._height}
-
-
-class SmallChair(IChair): # pylint: disable=too-few-public-methods
- """The Small Chair Concrete Class which implements the IChair interface"""
-
- def __init__(self):
- self._height = 40
- self._width = 40
- self._depth = 40
-
- def dimensions(self):
- return {"width": self._width, "depth": self._depth, "height": self._height}
+"The Factory Class"
+from small_chair import SmallChair
+from medium_chair import MediumChair
+from big_chair import BigChair
class ChairFactory: # pylint: disable=too-few-public-methods
- """Tha Factory Class"""
+ "The Factory Class"
@staticmethod
def get_chair(chair):
- """A static method to get a table"""
- try:
- if chair == "BigChair":
- return BigChair()
- if chair == "MediumChair":
- return MediumChair()
- if chair == "SmallChair":
- return SmallChair()
- raise AssertionError("Chair Not Found")
- except AssertionError as _e:
- print(_e)
+ "A static method to get a chair"
+ if chair == 'BigChair':
+ return BigChair()
+ if chair == 'MediumChair':
+ return MediumChair()
+ if chair == 'SmallChair':
+ return SmallChair()
return None
-
-
-if __name__ == "__main__":
- CHAIR_FACTORY = ChairFactory().get_chair("SmallChair")
- print(CHAIR_FACTORY.dimensions())
diff --git a/factory/classes_chair_factory.dot b/factory/classes_chair_factory.dot
deleted file mode 100644
index fae051c..0000000
--- a/factory/classes_chair_factory.dot
+++ /dev/null
@@ -1,16 +0,0 @@
-digraph "classes_chair_factory" {
-charset="utf-8"
-rankdir=BT
-{rank=same; 0,3,4 }
-"0" [label="{Factory1|\l|get_object()\l}", shape="record"];
-"2" [label="{IChair|\l|get_object()\l}", shape="record"];
-"3" [label="{Factory2|\l|get_object()\l}", shape="record"];
-"4" [label="{Factory2|\l|get_object()\l}", shape="record"];
-"1" [label="{Factory|\l|get_object()\l}", shape="record"];
-"0" -> "2" [arrowhead="empty", arrowtail="none"];
-"3" -> "2" [arrowhead="empty", arrowtail="none"];
-"4" -> "2" [arrowhead="empty", arrowtail="none"];
-"1" -> "0" [arrowhead="open", arrowtail="none", style="dashed"];
-"1" -> "3" [arrowhead="open", arrowtail="none", style="dashed"];
-"1" -> "4" [arrowhead="open", arrowtail="none", style="dashed"];
-}
diff --git a/factory/classes_factory.dot b/factory/classes_factory.dot
deleted file mode 100644
index eb59de9..0000000
--- a/factory/classes_factory.dot
+++ /dev/null
@@ -1,12 +0,0 @@
-digraph "classes_chair_factory" {
-charset="utf-8"
-rankdir=BT
-"0" [label="{Factory|\l|get_object()\l}", shape="record"];
-"1" [label="{IObject|\l|dimensions()\l}", shape="record"];
-"2" [label="{Object1|\l|dimensions()\l}", shape="record"];
-"3" [label="{Object2|\l|dimensions()\l}", shape="record"];
-"4" [label="{Object3|\l|dimensions()\l}", shape="record"];
-"2" -> "1" [arrowhead="empty", arrowtail="none"];
-"3" -> "1" [arrowhead="empty", arrowtail="none"];
-"4" -> "1" [arrowhead="empty", arrowtail="none"];
-}
diff --git a/factory/client.py b/factory/client.py
new file mode 100644
index 0000000..c4f9bc5
--- /dev/null
+++ b/factory/client.py
@@ -0,0 +1,7 @@
+"Factory Use Case Example Code"
+
+from chair_factory import ChairFactory
+
+# The Client
+CHAIR = ChairFactory.get_chair("SmallChair")
+print(CHAIR.get_dimensions())
diff --git a/factory/factory.png b/factory/factory.png
deleted file mode 100644
index 90cc146..0000000
Binary files a/factory/factory.png and /dev/null differ
diff --git a/factory/factory_concept.py b/factory/factory_concept.py
new file mode 100644
index 0000000..6dbb529
--- /dev/null
+++ b/factory/factory_concept.py
@@ -0,0 +1,63 @@
+# pylint: disable=too-few-public-methods
+# pylint: disable=arguments-differ
+"The Factory Concept"
+from abc import ABCMeta, abstractmethod
+
+
+class IProduct(metaclass=ABCMeta):
+ "A Hypothetical Class Interface (Product)"
+
+ @staticmethod
+ @abstractmethod
+ def create_object():
+ "An abstract interface method"
+
+
+class ConcreteProductA(IProduct):
+ "A Concrete Class that implements the IProduct interface"
+
+ def __init__(self):
+ self.name = "ConcreteProductA"
+
+ def create_object(self):
+ return self
+
+
+class ConcreteProductB(IProduct):
+ "A Concrete Class that implements the IProduct interface"
+
+ def __init__(self):
+ self.name = "ConcreteProductB"
+
+ def create_object(self):
+ return self
+
+
+class ConcreteProductC(IProduct):
+ "A Concrete Class that implements the IProduct interface"
+
+ def __init__(self):
+ self.name = "ConcreteProductC"
+
+ def create_object(self):
+ return self
+
+
+class Creator:
+ "The Factory Class"
+
+ @staticmethod
+ def create_object(some_property):
+ "A static method to get a concrete product"
+ if some_property == 'a':
+ return ConcreteProductA()
+ if some_property == 'b':
+ return ConcreteProductB()
+ if some_property == 'c':
+ return ConcreteProductC()
+ return None
+
+
+# The Client
+PRODUCT = Creator.create_object('b')
+print(PRODUCT.name)
diff --git a/factory/factory_pattern.png b/factory/factory_pattern.png
deleted file mode 100644
index cc01bdb..0000000
Binary files a/factory/factory_pattern.png and /dev/null differ
diff --git a/factory/factory_pattern_chair.png b/factory/factory_pattern_chair.png
deleted file mode 100644
index 98ac417..0000000
Binary files a/factory/factory_pattern_chair.png and /dev/null differ
diff --git a/factory/interface_chair.py b/factory/interface_chair.py
new file mode 100644
index 0000000..978e020
--- /dev/null
+++ b/factory/interface_chair.py
@@ -0,0 +1,12 @@
+# pylint: disable=too-few-public-methods
+"The Chair Interface"
+from abc import ABCMeta, abstractmethod
+
+
+class IChair(metaclass=ABCMeta):
+ "The Chair Interface (Product)"
+
+ @staticmethod
+ @abstractmethod
+ def get_dimensions():
+ "A static interface method"
diff --git a/factory/medium_chair.py b/factory/medium_chair.py
new file mode 100644
index 0000000..c6c6e1f
--- /dev/null
+++ b/factory/medium_chair.py
@@ -0,0 +1,19 @@
+# pylint: disable=too-few-public-methods
+"A Class of Chair"
+from interface_chair import IChair
+
+
+class MediumChair(IChair):
+ "The Medium Chair Concrete Class implements the IChair interface"
+
+ def __init__(self):
+ self._height = 60
+ self._width = 60
+ self._depth = 60
+
+ def get_dimensions(self):
+ return {
+ "width": self._width,
+ "depth": self._depth,
+ "height": self._height
+ }
diff --git a/factory/small_chair.py b/factory/small_chair.py
new file mode 100644
index 0000000..9db8d18
--- /dev/null
+++ b/factory/small_chair.py
@@ -0,0 +1,19 @@
+# pylint: disable=too-few-public-methods
+"A Class of Chair"
+from interface_chair import IChair
+
+
+class SmallChair(IChair):
+ "The Small Chair Concrete Class implements the IChair interface"
+
+ def __init__(self):
+ self._height = 40
+ self._width = 40
+ self._depth = 40
+
+ def get_dimensions(self):
+ return {
+ "width": self._width,
+ "depth": self._depth,
+ "height": self._height
+ }
diff --git a/flyweight/README.md b/flyweight/README.md
new file mode 100644
index 0000000..e0fbbc2
--- /dev/null
+++ b/flyweight/README.md
@@ -0,0 +1,89 @@
+# Flyweight Design Pattern
+
+## Videos
+
+Section | Video Links
+-|-
+Flyweight Overview |
+Flyweight Use Case |
+String Justification |
+
+## Book
+
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
+
+## Overview
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Flyweight UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Output
+
+``` bash
+python ./flyweight/flyweight_concept.py
+abracadabra
+abracadabra has 11 letters
+FlyweightFactory has 5 flyweights
+```
+
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./flyweight/client.py
+-----------------------------------------
+|abra | 112233 | cadabra|
+|racadab | 12345 | 332211|
+|cadabra | 445566 | aa 22 bb|
+-----------------------------------------
+FlyweightFactory has 12 flyweights
+```
+
+## New Coding Concepts
+
+### String Justification
+
+In [/flyweight/column.py](/flyweight/column.py), there are commands `center()`, `ljust()` and `rjust()` .
+
+These are special commands on strings that allow you to pad strings and align them left, right, center depending on total string length.
+
+eg,
+
+``` powershell
+>>> "abcd".center(10)
+' abcd '
+```
+
+``` powershell
+>>> "abcd".rjust(10)
+' abcd'
+```
+
+``` powershell
+>>> "abcd".ljust(10)
+'abcd '
+```
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/flyweight/client.py b/flyweight/client.py
new file mode 100644
index 0000000..b84d864
--- /dev/null
+++ b/flyweight/client.py
@@ -0,0 +1,30 @@
+"The Flyweight Use Case Example"
+
+from table import Table
+from flyweight_factory import FlyweightFactory
+
+TABLE = Table(3, 3)
+
+TABLE.rows[0].columns[0].data = "abra"
+TABLE.rows[0].columns[1].data = "112233"
+TABLE.rows[0].columns[2].data = "cadabra"
+TABLE.rows[1].columns[0].data = "racadab"
+TABLE.rows[1].columns[1].data = "12345"
+TABLE.rows[1].columns[2].data = "332211"
+TABLE.rows[2].columns[0].data = "cadabra"
+TABLE.rows[2].columns[1].data = "445566"
+TABLE.rows[2].columns[2].data = "aa 22 bb"
+
+TABLE.rows[0].columns[0].justify = 1
+TABLE.rows[1].columns[0].justify = 1
+TABLE.rows[2].columns[0].justify = 1
+TABLE.rows[0].columns[2].justify = 2
+TABLE.rows[1].columns[2].justify = 2
+TABLE.rows[2].columns[2].justify = 2
+TABLE.rows[0].columns[1].width = 15
+TABLE.rows[1].columns[1].width = 15
+TABLE.rows[2].columns[1].width = 15
+
+TABLE.draw()
+
+print(f"FlyweightFactory has {FlyweightFactory.get_count()} flyweights")
diff --git a/flyweight/column.py b/flyweight/column.py
new file mode 100644
index 0000000..36f24bc
--- /dev/null
+++ b/flyweight/column.py
@@ -0,0 +1,25 @@
+"A Column that is used in a Row"
+from flyweight_factory import FlyweightFactory
+
+class Column(): # pylint: disable=too-few-public-methods
+ """
+ The columns are the contexts.
+ They will share the Flyweights via the FlyweightsFactory.
+ `data`, `width` and `justify` are extrinsic values. They are outside
+ of the flyweights.
+ """
+
+ def __init__(self, data="", width=11, justify=0) -> None:
+ self.data = data
+ self.width = width
+ self.justify = justify # 0:center, 1:left, 2:right
+
+ def get_data(self):
+ "Get the flyweight value from the factory, and apply the extrinsic values"
+ ret = ""
+ for data in self.data:
+ ret = ret + FlyweightFactory.get_flyweight(data).code
+ ret = f"{ret.center(self.width)}" if self.justify == 0 else ret
+ ret = f"{ret.ljust(self.width)}" if self.justify == 1 else ret
+ ret = f"{ret.rjust(self.width)}" if self.justify == 2 else ret
+ return ret
diff --git a/flyweight/flyweight.py b/flyweight/flyweight.py
new file mode 100644
index 0000000..e7e33de
--- /dev/null
+++ b/flyweight/flyweight.py
@@ -0,0 +1,8 @@
+"The Flyweight that contains an intrinsic value called code"
+
+
+class Flyweight(): # pylint: disable=too-few-public-methods
+ "The Flyweight that contains an intrinsic value called code"
+
+ def __init__(self, code: str) -> None:
+ self.code = code
diff --git a/flyweight/flyweight_concept.py b/flyweight/flyweight_concept.py
new file mode 100644
index 0000000..daa478f
--- /dev/null
+++ b/flyweight/flyweight_concept.py
@@ -0,0 +1,62 @@
+# pylint: disable=too-few-public-methods
+"The Flyweight Concept"
+
+
+class IFlyweight():
+ "Nothing to implement"
+
+
+class Flyweight(IFlyweight):
+ "The Concrete Flyweight"
+
+ def __init__(self, code: str) -> None:
+ self.code = code
+
+
+class FlyweightFactory():
+ "Creating the FlyweightFactory as a singleton"
+
+ _flyweights: dict[str, Flyweight] = {} # Python 3.9
+ # _flyweights = {} # Python 3.8 or earlier
+
+ def __new__(cls):
+ return cls
+
+ @classmethod
+ def get_flyweight(cls, code: str) -> Flyweight:
+ "A static method to get a flyweight based on a code"
+ if not code in cls._flyweights:
+ cls._flyweights[code] = Flyweight(code)
+ return cls._flyweights[code]
+
+ @classmethod
+ def get_count(cls) -> int:
+ "Return the number of flyweights in the cache"
+ return len(cls._flyweights)
+
+
+class Context():
+ """
+ An example context that holds references to the flyweights in a
+ particular order and converts the code to an ascii letter
+ """
+
+ def __init__(self, codes: str) -> None:
+ self.codes = list(codes)
+
+ def output(self):
+ "The context specific output that uses flyweights"
+ ret = ""
+ for code in self.codes:
+ ret = ret + FlyweightFactory.get_flyweight(code).code
+ return ret
+
+
+# The Client
+CONTEXT = Context("abracadabra")
+
+# use flyweights in a context
+print(CONTEXT.output())
+
+print(f"abracadabra has {len('abracadabra')} letters")
+print(f"FlyweightFactory has {FlyweightFactory.get_count()} flyweights")
diff --git a/flyweight/flyweight_factory.py b/flyweight/flyweight_factory.py
new file mode 100644
index 0000000..9206d38
--- /dev/null
+++ b/flyweight/flyweight_factory.py
@@ -0,0 +1,24 @@
+"Creating the FlyweightFactory as a singleton"
+from flyweight import Flyweight
+
+
+class FlyweightFactory():
+ "Creating the FlyweightFactory as a singleton"
+
+ _flyweights: dict[str, Flyweight] = {} # Python 3.9
+ # _flyweights = {} # Python 3.8 or earlier
+
+ def __new__(cls):
+ return cls
+
+ @classmethod
+ def get_flyweight(cls, code: str) -> Flyweight:
+ "A static method to get a flyweight based on a code"
+ if not code in cls._flyweights:
+ cls._flyweights[code] = Flyweight(code)
+ return cls._flyweights[code]
+
+ @classmethod
+ def get_count(cls) -> int:
+ "Return the number of flyweights in the cache"
+ return len(cls._flyweights)
diff --git a/flyweight/row.py b/flyweight/row.py
new file mode 100644
index 0000000..5ea7df2
--- /dev/null
+++ b/flyweight/row.py
@@ -0,0 +1,18 @@
+"A Row in the Table"
+from column import Column
+
+
+class Row(): # pylint: disable=too-few-public-methods
+ "A Row in the Table"
+
+ def __init__(self, column_count: int) -> None:
+ self.columns = []
+ for _ in range(column_count):
+ self.columns.append(Column())
+
+ def get_data(self):
+ "Format the row before returning it to the table"
+ ret = ""
+ for column in self.columns:
+ ret = f"{ret}{column.get_data()}|"
+ return ret
diff --git a/flyweight/table.py b/flyweight/table.py
new file mode 100644
index 0000000..96c455f
--- /dev/null
+++ b/flyweight/table.py
@@ -0,0 +1,27 @@
+"A Formatted Table that includes rows and columns"
+
+from row import Row
+
+
+class Table(): # pylint: disable=too-few-public-methods
+ "A Formatted Table"
+
+ def __init__(self, row_count: int, column_count: int) -> None:
+ self.rows = []
+ for _ in range(row_count):
+ self.rows.append(Row(column_count))
+
+ def draw(self):
+ "Draws the table formatted in the console"
+ max_row_length = 0
+ rows = []
+ for row in self.rows:
+ row_data = row.get_data()
+ rows.append(f"|{row_data}")
+ row_length = len(row_data) + 1
+ if max_row_length < row_length:
+ max_row_length = row_length
+ print("-" * max_row_length)
+ for row in rows:
+ print(row)
+ print("-" * max_row_length)
diff --git a/img/abstract_factory_concept.svg b/img/abstract_factory_concept.svg
new file mode 100644
index 0000000..3e2903d
--- /dev/null
+++ b/img/abstract_factory_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/abstract_furniture_factory.svg b/img/abstract_furniture_factory.svg
new file mode 100644
index 0000000..6c3b9e2
--- /dev/null
+++ b/img/abstract_furniture_factory.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/adapter_concept.svg b/img/adapter_concept.svg
new file mode 100644
index 0000000..df0bdda
--- /dev/null
+++ b/img/adapter_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/adapter_example.svg b/img/adapter_example.svg
new file mode 100644
index 0000000..61350eb
--- /dev/null
+++ b/img/adapter_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/aggregates.svg b/img/aggregates.svg
new file mode 100644
index 0000000..58e6d32
--- /dev/null
+++ b/img/aggregates.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/basic_class.svg b/img/basic_class.svg
new file mode 100644
index 0000000..d717b4c
--- /dev/null
+++ b/img/basic_class.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/bridge_concept.svg b/img/bridge_concept.svg
new file mode 100644
index 0000000..b45ea91
--- /dev/null
+++ b/img/bridge_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/bridge_example.svg b/img/bridge_example.svg
new file mode 100644
index 0000000..3bd2b6c
--- /dev/null
+++ b/img/bridge_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/builder_concept.svg b/img/builder_concept.svg
new file mode 100644
index 0000000..4554c02
--- /dev/null
+++ b/img/builder_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/builder_example.svg b/img/builder_example.svg
new file mode 100644
index 0000000..df60679
--- /dev/null
+++ b/img/builder_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/by-nc.png b/img/by-nc.png
new file mode 100644
index 0000000..6b175f6
Binary files /dev/null and b/img/by-nc.png differ
diff --git a/img/chain_of_responsibility_concept.svg b/img/chain_of_responsibility_concept.svg
new file mode 100644
index 0000000..e2bee07
--- /dev/null
+++ b/img/chain_of_responsibility_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/chain_of_responsibility_example.svg b/img/chain_of_responsibility_example.svg
new file mode 100644
index 0000000..2b62cbf
--- /dev/null
+++ b/img/chain_of_responsibility_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/class_extends.svg b/img/class_extends.svg
new file mode 100644
index 0000000..9c9b441
--- /dev/null
+++ b/img/class_extends.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/class_implements.svg b/img/class_implements.svg
new file mode 100644
index 0000000..c0e1c63
--- /dev/null
+++ b/img/class_implements.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/command_concept.svg b/img/command_concept.svg
new file mode 100644
index 0000000..bd326c1
--- /dev/null
+++ b/img/command_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/command_example.svg b/img/command_example.svg
new file mode 100644
index 0000000..1e14783
--- /dev/null
+++ b/img/command_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/composite_concept.svg b/img/composite_concept.svg
new file mode 100644
index 0000000..482c100
--- /dev/null
+++ b/img/composite_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/composite_example.svg b/img/composite_example.svg
new file mode 100644
index 0000000..dfe3511
--- /dev/null
+++ b/img/composite_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/composition.svg b/img/composition.svg
new file mode 100644
index 0000000..8a3836f
--- /dev/null
+++ b/img/composition.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/decorator_concept.svg b/img/decorator_concept.svg
new file mode 100644
index 0000000..1d69fed
--- /dev/null
+++ b/img/decorator_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/decorator_example.svg b/img/decorator_example.svg
new file mode 100644
index 0000000..a96e383
--- /dev/null
+++ b/img/decorator_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/design_patterns_in_python_book.jpg b/img/design_patterns_in_python_book.jpg
new file mode 100644
index 0000000..e9f2427
Binary files /dev/null and b/img/design_patterns_in_python_book.jpg differ
diff --git a/img/design_patterns_in_python_book_125x178.jpg b/img/design_patterns_in_python_book_125x178.jpg
new file mode 100644
index 0000000..0cf8aaa
Binary files /dev/null and b/img/design_patterns_in_python_book_125x178.jpg differ
diff --git a/img/directed_association.svg b/img/directed_association.svg
new file mode 100644
index 0000000..97025f2
--- /dev/null
+++ b/img/directed_association.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/dp_python_125.gif b/img/dp_python_125.gif
new file mode 100644
index 0000000..601a5b3
Binary files /dev/null and b/img/dp_python_125.gif differ
diff --git a/img/dp_python_250.jpg b/img/dp_python_250.jpg
new file mode 100644
index 0000000..2305ab0
Binary files /dev/null and b/img/dp_python_250.jpg differ
diff --git a/img/dp_typescript_250.jpg b/img/dp_typescript_250.jpg
new file mode 100644
index 0000000..ccb35c1
Binary files /dev/null and b/img/dp_typescript_250.jpg differ
diff --git a/img/facade_concept.svg b/img/facade_concept.svg
new file mode 100644
index 0000000..9802e8c
--- /dev/null
+++ b/img/facade_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/facade_example.svg b/img/facade_example.svg
new file mode 100644
index 0000000..6b58d66
--- /dev/null
+++ b/img/facade_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/factory_concept.svg b/img/factory_concept.svg
new file mode 100644
index 0000000..aa55c0e
--- /dev/null
+++ b/img/factory_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/factory_example.svg b/img/factory_example.svg
new file mode 100644
index 0000000..5289c48
--- /dev/null
+++ b/img/factory_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/factory_swimlane.svg b/img/factory_swimlane.svg
new file mode 100644
index 0000000..c1b00f2
--- /dev/null
+++ b/img/factory_swimlane.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/flag_au.gif b/img/flag_au.gif
new file mode 100644
index 0000000..bcfe17a
Binary files /dev/null and b/img/flag_au.gif differ
diff --git a/img/flag_ca.gif b/img/flag_ca.gif
new file mode 100644
index 0000000..c1a9363
Binary files /dev/null and b/img/flag_ca.gif differ
diff --git a/img/flag_de.gif b/img/flag_de.gif
new file mode 100644
index 0000000..d2a4c7b
Binary files /dev/null and b/img/flag_de.gif differ
diff --git a/img/flag_es.gif b/img/flag_es.gif
new file mode 100644
index 0000000..894dfcf
Binary files /dev/null and b/img/flag_es.gif differ
diff --git a/img/flag_fr.gif b/img/flag_fr.gif
new file mode 100644
index 0000000..ccc1808
Binary files /dev/null and b/img/flag_fr.gif differ
diff --git a/img/flag_in.gif b/img/flag_in.gif
new file mode 100644
index 0000000..21008e1
Binary files /dev/null and b/img/flag_in.gif differ
diff --git a/img/flag_it.gif b/img/flag_it.gif
new file mode 100644
index 0000000..1043bce
Binary files /dev/null and b/img/flag_it.gif differ
diff --git a/img/flag_jp.gif b/img/flag_jp.gif
new file mode 100644
index 0000000..5c98a2d
Binary files /dev/null and b/img/flag_jp.gif differ
diff --git a/img/flag_uk.gif b/img/flag_uk.gif
new file mode 100644
index 0000000..7938447
Binary files /dev/null and b/img/flag_uk.gif differ
diff --git a/img/flag_us.gif b/img/flag_us.gif
new file mode 100644
index 0000000..d6b659b
Binary files /dev/null and b/img/flag_us.gif differ
diff --git a/img/flyweight_concept.svg b/img/flyweight_concept.svg
new file mode 100644
index 0000000..9f12f34
--- /dev/null
+++ b/img/flyweight_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/flyweight_example.svg b/img/flyweight_example.svg
new file mode 100644
index 0000000..79842e5
--- /dev/null
+++ b/img/flyweight_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/ide_hint.jpg b/img/ide_hint.jpg
new file mode 100644
index 0000000..aed9cda
Binary files /dev/null and b/img/ide_hint.jpg differ
diff --git a/img/interpreter_ast.svg b/img/interpreter_ast.svg
new file mode 100644
index 0000000..480ea2a
--- /dev/null
+++ b/img/interpreter_ast.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/interpreter_ast_roman_numeral.svg b/img/interpreter_ast_roman_numeral.svg
new file mode 100644
index 0000000..2aea838
--- /dev/null
+++ b/img/interpreter_ast_roman_numeral.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/interpreter_concept.svg b/img/interpreter_concept.svg
new file mode 100644
index 0000000..0f22d92
--- /dev/null
+++ b/img/interpreter_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/interpreter_example.svg b/img/interpreter_example.svg
new file mode 100644
index 0000000..bccd8b0
--- /dev/null
+++ b/img/interpreter_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/iterator_concept.svg b/img/iterator_concept.svg
new file mode 100644
index 0000000..1c37d05
--- /dev/null
+++ b/img/iterator_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/iterator_example.svg b/img/iterator_example.svg
new file mode 100644
index 0000000..396a041
--- /dev/null
+++ b/img/iterator_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/mediator_concept.svg b/img/mediator_concept.svg
new file mode 100644
index 0000000..050e622
--- /dev/null
+++ b/img/mediator_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/mediator_example.svg b/img/mediator_example.svg
new file mode 100644
index 0000000..364b8e6
--- /dev/null
+++ b/img/mediator_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/memento_concept.svg b/img/memento_concept.svg
new file mode 100644
index 0000000..4758897
--- /dev/null
+++ b/img/memento_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/memento_example.svg b/img/memento_example.svg
new file mode 100644
index 0000000..dd3adf3
--- /dev/null
+++ b/img/memento_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/observer_concept.svg b/img/observer_concept.svg
new file mode 100644
index 0000000..5014d46
--- /dev/null
+++ b/img/observer_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/observer_example.svg b/img/observer_example.svg
new file mode 100644
index 0000000..080c4f9
--- /dev/null
+++ b/img/observer_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/prototype_concept.svg b/img/prototype_concept.svg
new file mode 100644
index 0000000..62c5509
--- /dev/null
+++ b/img/prototype_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/prototype_example.svg b/img/prototype_example.svg
new file mode 100644
index 0000000..feb1dda
--- /dev/null
+++ b/img/prototype_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/proxy_concept.svg b/img/proxy_concept.svg
new file mode 100644
index 0000000..01ae8e8
--- /dev/null
+++ b/img/proxy_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/proxy_example.svg b/img/proxy_example.svg
new file mode 100644
index 0000000..bdc4472
--- /dev/null
+++ b/img/proxy_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/pseudocode_annotation.svg b/img/pseudocode_annotation.svg
new file mode 100644
index 0000000..abfd818
--- /dev/null
+++ b/img/pseudocode_annotation.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/singleton_concept.svg b/img/singleton_concept.svg
new file mode 100644
index 0000000..8f8d9eb
--- /dev/null
+++ b/img/singleton_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/singleton_example.svg b/img/singleton_example.svg
new file mode 100644
index 0000000..833eba2
--- /dev/null
+++ b/img/singleton_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/skillshare_btn.png b/img/skillshare_btn.png
deleted file mode 100644
index c7a65ef..0000000
Binary files a/img/skillshare_btn.png and /dev/null differ
diff --git a/img/skillshare_btn_sm.gif b/img/skillshare_btn_sm.gif
new file mode 100644
index 0000000..aa17453
Binary files /dev/null and b/img/skillshare_btn_sm.gif differ
diff --git a/img/state_concept.svg b/img/state_concept.svg
new file mode 100644
index 0000000..a5ce26d
--- /dev/null
+++ b/img/state_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/state_example.svg b/img/state_example.svg
new file mode 100644
index 0000000..54b794d
--- /dev/null
+++ b/img/state_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/strategy_concept.svg b/img/strategy_concept.svg
new file mode 100644
index 0000000..1768c77
--- /dev/null
+++ b/img/strategy_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/strategy_example.svg b/img/strategy_example.svg
new file mode 100644
index 0000000..8abe986
--- /dev/null
+++ b/img/strategy_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/template_concept.svg b/img/template_concept.svg
new file mode 100644
index 0000000..2bf87f7
--- /dev/null
+++ b/img/template_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/template_example.svg b/img/template_example.svg
new file mode 100644
index 0000000..e5d42c0
--- /dev/null
+++ b/img/template_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/udemy_btn.png b/img/udemy_btn.png
deleted file mode 100644
index 68c7c24..0000000
Binary files a/img/udemy_btn.png and /dev/null differ
diff --git a/img/udemy_btn_sm.gif b/img/udemy_btn_sm.gif
new file mode 100644
index 0000000..bab7b91
Binary files /dev/null and b/img/udemy_btn_sm.gif differ
diff --git a/img/visitor_concept.svg b/img/visitor_concept.svg
new file mode 100644
index 0000000..1b64e44
--- /dev/null
+++ b/img/visitor_concept.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/visitor_example.svg b/img/visitor_example.svg
new file mode 100644
index 0000000..63c1f8a
--- /dev/null
+++ b/img/visitor_example.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/img/yt_btn_sm.gif b/img/yt_btn_sm.gif
new file mode 100644
index 0000000..20b119a
Binary files /dev/null and b/img/yt_btn_sm.gif differ
diff --git a/interpreter/README.md b/interpreter/README.md
index 35d1f81..c49e7d8 100644
--- a/interpreter/README.md
+++ b/interpreter/README.md
@@ -1,25 +1,174 @@
-terminal or a non-terminal expression
+# Interpreter Design Pattern
-terminal means the terminated command.
-non-termainl commands are the same as the composite commands
-composite, can be reordered in hierachy like files and folders in a filesystem
+## Videos
+Section | Video Links
+-|-
+Interpreter Overview |
+Interpreter Use Case |
+String Slicing |
+__repr__ Dunder Method |
-the terminal expression is the final result
-the non-terminal expressionsa are the sub expressions
-select [list] ffrom [list] where [list] = [list]
+## Book
-You want to create your own compiler
-compiles stuff
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
-Each abstract base class of the interpreter defines the interpret() method, and each concrete class inheriting from the base class implements the interpret() method, which translates the specific part of the language required at the moment.
+## Overview
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
-Context
-AbstractExpression
- interpret
-Concretclasses(AbstractExpression)
- interpret
+## Terminology
-Roman numeral converter
-converts roman numerals to int
\ No newline at end of file
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Interpreter UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Output
+
+``` bash
+python ./interpreter/interpreter_concept.py
+5 + 4 - 3 + 7 - 2
+['5', '+', '4', '-', '3', '+', '7', '-', '2']
+11
+((((5 Add 4) Subtract 3) Add 7) Subtract 2)
+```
+
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+
+
+## Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./interpreter/client.py
+5 + IV - 3 + VII - 2
+['5', '+', 'IV', '-', '3', '+', 'VII', '-', '2']
+11
+((((5 Add IV(4)) Subtract 3) Add VII(7)) Subtract 2)
+```
+
+## New Coding Concepts
+
+### String Slicing
+
+Sometimes you want part of a string. In the example code, when I am interpreting the roman numerals, I am comparing the first one or two characters in the context with `IV` or `CM` or many other roman numeral combinations. If the match is true then I continue with further commands.
+
+The format is
+
+``` python
+string[start: end: step]
+```
+
+E.g., the string may be
+
+``` text
+MMMCMXCIX
+```
+
+and I want the first three characters,
+
+``` python
+test = "MMMCMXCIX"
+print(test[0: 3])
+```
+
+Outputs
+
+``` text
+MMM
+```
+
+or I want the last 4 characters
+
+``` python
+test = "MMMCMXCIX"
+print(test[-4:])
+```
+
+Outputs
+
+``` text
+XCIX
+```
+
+or I want a section in the middle
+
+``` python
+test = "MMMCMXCIX"
+print(test[2:5])
+```
+
+Outputs
+
+``` text
+MCM
+```
+
+or stepped
+
+``` python
+test = "MMMCMXCIX"
+print(test[2:9:2])
+```
+
+Outputs
+
+``` text
+MMCX
+```
+
+or even reversed
+
+``` python
+test = "MMMCMXCIX"
+print(test[::-1])
+```
+
+Outputs
+
+``` text
+XICXMCMMM
+```
+
+The technique is very common in examples of Python source code throughout the internet. So, when you see the `[]` with numbers and colons inside, eg, `[:-1:]`, it is likely to do with extracting a portion of a data structure.
+
+Note that the technique also works on [Lists](/builder#python-list) and [Tuples](/bridge#python-tuple).
+
+``` python
+test = [1,2,3,4,5,6,7,8,9]
+print(test[0: 3])
+print(test[-4:])
+print(test[2:5])
+print(test[2:9:2])
+print(test[::-1])
+print(test[:-1:])
+```
+
+Outputs
+
+``` text
+[1, 2, 3]
+[6, 7, 8, 9]
+[3, 4, 5]
+[3, 5, 7, 9]
+[9, 8, 7, 6, 5, 4, 3, 2, 1]
+[1, 2, 3, 4, 5, 6, 7, 8]
+```
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/interpreter/abstract_expression.py b/interpreter/abstract_expression.py
new file mode 100644
index 0000000..2b5009c
--- /dev/null
+++ b/interpreter/abstract_expression.py
@@ -0,0 +1,13 @@
+"An Abstract Expression"
+# pylint: disable=too-few-public-methods
+class AbstractExpression():
+ """
+ All Terminal and Non-Terminal expressions will implement an
+ `interpret` method
+ """
+ @staticmethod
+ def interpret():
+ """
+ The `interpret` method gets called recursively for
+ each AbstractExpression
+ """
diff --git a/interpreter/add.py b/interpreter/add.py
new file mode 100644
index 0000000..958094b
--- /dev/null
+++ b/interpreter/add.py
@@ -0,0 +1,16 @@
+"Add Expression. This is a Non-Terminal Expression"
+from abstract_expression import AbstractExpression
+
+
+class Add(AbstractExpression):
+ "Non-Terminal Expression."
+
+ def __init__(self, left, right):
+ self.left = left
+ self.right = right
+
+ def interpret(self):
+ return self.left.interpret() + self.right.interpret()
+
+ def __repr__(self):
+ return f"({self.left} Add {self.right})"
diff --git a/interpreter/client.py b/interpreter/client.py
new file mode 100644
index 0000000..0499d63
--- /dev/null
+++ b/interpreter/client.py
@@ -0,0 +1,20 @@
+"The Interpreter Pattern Use Case Example"
+
+from sentence_parser import Parser
+
+# The sentence complies with a simple grammar of
+# Number -> Operator -> Number -> etc,
+SENTENCE = "5 + IV - 3 + VII - 2"
+# SENTENCE = "V + IV - III + 7 - II"
+# SENTENCE= "CIX + V"
+# SENTENCE = "CIX + V - 3 + VII - 2"
+# SENTENCE = "MMMCMXCIX - CXIX + MCXXII - MMMCDXII - XVIII - CCXXXV"
+print(SENTENCE)
+
+AST_ROOT = Parser.parse(SENTENCE)
+
+# Interpret recursively through the full AST starting from the root.
+print(AST_ROOT.interpret())
+
+# Print out a representation of the AST_ROOT
+print(AST_ROOT)
diff --git a/interpreter/interpreter.py b/interpreter/interpreter.py
deleted file mode 100644
index 1762a85..0000000
--- a/interpreter/interpreter.py
+++ /dev/null
@@ -1,141 +0,0 @@
-from abc import ABCMeta, abstractmethod
-
-
-class Context:
- def __init__(self):
- self._input = ""
- self._output = 0
-
- # using property decorator
- # a getter function
- @property
- def Input(self):
- return self._input
-
- # a setter function
- @Input.setter
- def Input(self, a):
- self._input = a
-
- @property
- def Output(self):
- return self._input
-
- # a setter function
- @Output.setter
- def Output(self, a):
- self._input = a
-
-
-class Expression(metaclass=ABCMeta):
- @staticmethod
- @abstractmethod
- def One():
- ""
- @staticmethod
- @abstractmethod
- def Four():
- ""
- @staticmethod
- @abstractmethod
- def Five():
- ""
- @staticmethod
- @abstractmethod
- def Nine():
- ""
- @staticmethod
- @abstractmethod
- def Multiplier():
- ""
-
- @staticmethod
- @abstractmethod
- def Interpret(context:Context):
- ""
-
-class HundredExpression(Expression):
- def One(self):
- return "C"
- def Four(self):
- return "CD"
- def Five(self):
- return "D"
- def Nine(self):
- return "CM"
- def Multiplier(self):
- return 100
-
- def Interpret(self, context:Context):
- if context.Input.Length == 0:
- return
-
- if context.Input.StartsWith(self.Nine()):
- context.Output += (9 * self.Multiplier())
- context.Input = context.Input.Substring(2)
- elif context.Input.StartsWith(self.Four()):
- context.Output += (4 * self.Multiplier())
- context.Input = context.Input.Substring(2)
- elif context.Input.StartsWith(self.Five()):
- context.Output += (5 * self.Multiplier())
- context.Input = context.Input.Substring(1)
-
- while context.Input.StartsWith(self.One()):
- context.Output += (1 * self.Multiplier())
- context.Input = context.Input.Substring(1)
-
-# class OneExpression : Expression
-# {
-# public override string One() { return "I"; }
-# public override string Four() { return "IV"; }
-# public override string Five() { return "V"; }
-# public override string Nine() { return "IX"; }
-# public override int Multiplier() { return 1; }
-# }
-
-# class TenExpression : Expression
-# {
-# public override string One() { return "X"; }
-# public override string Four() { return "XL"; }
-# public override string Five() { return "L"; }
-# public override string Nine() { return "XC"; }
-# public override int Multiplier() { return 10; }
-# }
-
-# class ThousandExpression : Expression
-# {
-# public override string One() { return "M"; }
-# public override string Four() { return " "; }
-# public override string Five() { return " "; }
-# public override string Nine() { return " "; }
-# public override int Multiplier() { return 1000; }
-# }
-
-# class Program
-# {
-# static void Main()
-# {
-# string roman = null;
-
-# while (!string.IsNullOrEmpty(roman = Console.ReadLine()))
-# {
-# Context context = new Context(roman);
-
-# List
+Iterator Use Case |
+Python iter() Function |
-**has_next** returns a value, usually a boolean indicating if the iterable is at the end of the list or not.
+## Book
-The benefits of using the Iterator pattern is that the client, can traverse an aggregate without needing to understand it's internal representation and data structures.
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
-
\ No newline at end of file
+## Overview
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Iterator UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Output
+
+``` bash
+python ./iterator/iterator_concept.py
+This method has been invoked
+This method has been invoked
+This method has been invoked
+This method has been invoked
+```
+
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./iterator/client.py
+2, 4, 6, 8, 10, 1, 3, 5, 7, 9, 0, 2, 4, 6, 8, 10, 1, 3, 5, 7, 9, 0,
+```
+
+## New Coding Concepts
+
+### Python iter()
+
+Python [Lists](/builder#python-list), [Dictionaries](/singleton#python-dictionary), [Sets](/observer#python-set) and [Tuples](/bridge#python-tuple) are already iterable, so if you want basic iteration for use in a for loop, then you only need to add your objects into one of those and it can be used right away.
+
+``` python
+NAMES = ['SEAN','COSMO','EMMY']
+for name in NAMES:
+ print(name, end=", ")
+#SEAN, COSMO, EMMY,
+```
+
+also, you can instantiate an iterable from the List, Dictionary, Tuple or Set by using the [Python iter()](#python-iter) method, or its own `__iter__()` dunder method, and then iterate over that using the `__next__()` method.
+
+``` python
+NAMES = ['SEAN','COSMO','EMMY']
+ITERATOR = iter(NAMES)
+print(ITERATOR.__next__())
+print(ITERATOR.__next__())
+print(ITERATOR.__next__())
+```
+
+or
+
+``` python
+NAMES = ['SEAN','COSMO','EMMY']
+ITERATOR = NAMES.__iter__()
+print(ITERATOR.__next__())
+print(ITERATOR.__next__())
+print(ITERATOR.__next__())
+```
+
+The Python `iter()` method also can accept a `sentinel` parameter.
+
+The `sentinel` parameter is useful for dynamically created objects that are returned from an iterator and indicates where the last item is in the iterator by raising a `StopIteration` exception.
+
+Usage : `iter(object, sentinel)`
+
+When using the `sentinel`, the object passed as the first argument in the `iter()` method will need to be callable.
+
+``` python
+class Doubler():
+ count = 1
+
+ @classmethod
+ def next(cls):
+ cls.count *= 2
+ return cls.count
+
+ __call__ = next
+
+ITERATOR = iter(Doubler(), 32)
+print(list(ITERATOR))
+# Outputs [2, 4, 8, 16]
+```
+
+The `__call__ = next` line in the example above is setting the default method of the class to be `next` and that makes the class callable. See [Dunder __call__ Method](/state#dunder-__call__-method) for more information.
+
+Also note that the list being printed at the end is automatically filled from the iterator. The list constructor utilizes the default callable method and the `StopIteration` exception automatically during its creation without needing to write this in the code.
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/iterator/client.py b/iterator/client.py
new file mode 100644
index 0000000..f9be059
--- /dev/null
+++ b/iterator/client.py
@@ -0,0 +1,20 @@
+"The Iterator Pattern Concept"
+
+
+class NumberWheel(): # pylint: disable=too-few-public-methods
+ "The concrete iterator (iterable)"
+
+ def __init__(self):
+ self.index = 0
+
+ def next(self):
+ """Return a new number next in the wheel"""
+ self.index = self.index + 1
+ return self.index * 2 % 11
+
+
+# The Client
+NUMBERWHEEL = NumberWheel()
+
+for i in range(22):
+ print(NUMBERWHEEL.next(), end=", ")
diff --git a/iterator/iterator.dot b/iterator/iterator.dot
deleted file mode 100644
index 19b398b..0000000
--- a/iterator/iterator.dot
+++ /dev/null
@@ -1,7 +0,0 @@
-digraph "classes" {
-charset="utf-8"
-rankdir=BT
-"0" [label="{IIterator|\l|has_next()\lnext()\l}", shape="record"];
-"1" [label="{Iterable|index : int\lmaximum : int\l|has_next()\lnext()\l}", shape="record"];
-"1" -> "0" [arrowhead="empty", arrowtail="none"];
-}
diff --git a/iterator/iterator.png b/iterator/iterator.png
deleted file mode 100644
index e2e656f..0000000
Binary files a/iterator/iterator.png and /dev/null differ
diff --git a/iterator/iterator.py b/iterator/iterator.py
deleted file mode 100644
index 4cadb1f..0000000
--- a/iterator/iterator.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from abc import ABCMeta, abstractmethod
-
-class IIterator(metaclass=ABCMeta):
- @staticmethod
- @abstractmethod
- def has_next():
- """Returns Boolean whether at end of collection or not"""
-
- @staticmethod
- @abstractmethod
- def next():
- """Return the object in collection"""
-
-class Iterable(IIterator):
- def __init__(self):
- self.index = 0
- self.maximum = 7
-
- def next(self):
- if self.index < self.maximum:
- x = self.index
- self.index += 1
- return x
- else:
- raise Exception("AtEndOfIteratorException", "At End of Iterator")
-
- def has_next(self):
- return self.index < self.maximum
-
-ITERABLE = Iterable()
-
-while ITERABLE.has_next():
- print(ITERABLE.next())
-
-
-# print(ITERABLE.next())
-# print(ITERABLE.next())
-# print(ITERABLE.next())
-# print(ITERABLE.next())
-# print(ITERABLE.next())
-# print(ITERABLE.next())
-# print(ITERABLE.next())
-# print(ITERABLE.next())
-
-
\ No newline at end of file
diff --git a/iterator/iterator_concept.py b/iterator/iterator_concept.py
new file mode 100644
index 0000000..59ff128
--- /dev/null
+++ b/iterator/iterator_concept.py
@@ -0,0 +1,61 @@
+# pylint: disable=too-few-public-methods
+# pylint: disable=arguments-differ
+"The Iterator Pattern Concept"
+from abc import ABCMeta, abstractmethod
+
+
+class IIterator(metaclass=ABCMeta):
+ "An Iterator Interface"
+ @staticmethod
+ @abstractmethod
+ def has_next():
+ "Returns Boolean whether at end of collection or not"
+
+ @staticmethod
+ @abstractmethod
+ def next():
+ "Return the object in collection"
+
+
+class Iterable(IIterator):
+ "The concrete iterator (iterable)"
+
+ def __init__(self, aggregates):
+ self.index = 0
+ self.aggregates = aggregates
+
+ def next(self):
+ if self.index < len(self.aggregates):
+ aggregate = self.aggregates[self.index]
+ self.index += 1
+ return aggregate
+ raise Exception("AtEndOfIteratorException", "At End of Iterator")
+
+ def has_next(self):
+ return self.index < len(self.aggregates)
+
+
+class IAggregate(metaclass=ABCMeta):
+ "An interface that the aggregates should implement"
+ @staticmethod
+ @abstractmethod
+ def method():
+ "a method to implement"
+
+
+class Aggregate(IAggregate):
+ "A concrete object"
+ @staticmethod
+ def method():
+ print("This method has been invoked")
+
+
+# The Client
+AGGREGATES = [Aggregate(), Aggregate(), Aggregate(), Aggregate()]
+# AGGREGATES is a python list that is already iterable by default.
+
+# but we can create own own iterator on top anyway.
+ITERABLE = Iterable(AGGREGATES)
+
+while ITERABLE.has_next():
+ ITERABLE.next().method()
diff --git a/mediator/README.md b/mediator/README.md
index a3990f8..c62667a 100644
--- a/mediator/README.md
+++ b/mediator/README.md
@@ -1,9 +1,65 @@
# Mediator Design Pattern
-The mediator pattern is a behavioural pattern that defines an object that encapsulates how a set of objects interact.
+## Videos
-With the mediator pattern, communication between objects is encapsulated within a mediator object.
+Section | Video Links
+-|-
+Mediator Overview |
+Mediator Use Case |
-Objects communicate through the mediator rather than directly with each other.
+## Book
-
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
+
+## Overview
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Mediator UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Output
+
+``` bash
+python ./mediator/mediator_concept.py
+COLLEAGUE1 <--> Here is the Colleague2 specific data you asked for
+COLLEAGUE2 <--> Here is the Colleague1 specific data you asked for
+```
+
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./mediator/client.py
+Component1: >>> Out >>> : data A
+Component2: <<< In <<< : data A
+Component3: <<< In <<< : data A
+Component2: >>> Out >>> : data B
+Component3: <<< In <<< : data B
+Component1: <<< In <<< : data B
+Component3: >>> Out >>> : data C
+Component2: <<< In <<< : data C
+Component1: <<< In <<< : data C
+```
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/mediator/classes.dot b/mediator/classes.dot
deleted file mode 100644
index 65f09da..0000000
--- a/mediator/classes.dot
+++ /dev/null
@@ -1,8 +0,0 @@
-digraph "classes" {
-charset="utf-8"
-rankdir=BT
-"0" [label="{Component|mediator\lname\l|notify()\lreceive()\l}", shape="record"];
-"1" [label="{IComponent|\l|notify()\lreceive()\l}", shape="record"];
-"2" [label="{Mediator|components : list\l|add()\lnotify()\l}", shape="record"];
-"0" -> "1" [arrowhead="empty", arrowtail="none"];
-}
diff --git a/mediator/client.py b/mediator/client.py
new file mode 100644
index 0000000..2cf7662
--- /dev/null
+++ b/mediator/client.py
@@ -0,0 +1,15 @@
+"The Mediator Use Case Example"
+from component import Component
+from mediator import Mediator
+
+MEDIATOR = Mediator()
+COMPONENT1 = Component(MEDIATOR, "Component1")
+COMPONENT2 = Component(MEDIATOR, "Component2")
+COMPONENT3 = Component(MEDIATOR, "Component3")
+MEDIATOR.add(COMPONENT1)
+MEDIATOR.add(COMPONENT2)
+MEDIATOR.add(COMPONENT3)
+
+COMPONENT1.notify("data A")
+COMPONENT2.notify("data B")
+COMPONENT3.notify("data C")
diff --git a/mediator/component.py b/mediator/component.py
new file mode 100644
index 0000000..b9cf510
--- /dev/null
+++ b/mediator/component.py
@@ -0,0 +1,17 @@
+"Each component stays synchronized through a mediator"
+from interface_component import IComponent
+
+
+class Component(IComponent):
+ "Each component stays synchronized through a mediator"
+
+ def __init__(self, mediator, name):
+ self._mediator = mediator
+ self._name = name
+
+ def notify(self, message):
+ print(self._name + ": >>> Out >>> : " + message)
+ self._mediator.notify(message, self)
+
+ def receive(self, message):
+ print(self._name + ": <<< In <<< : " + message)
diff --git a/mediator/interface_component.py b/mediator/interface_component.py
new file mode 100644
index 0000000..4592d9d
--- /dev/null
+++ b/mediator/interface_component.py
@@ -0,0 +1,16 @@
+"An interface that each component will implement"
+from abc import ABCMeta, abstractmethod
+
+
+class IComponent(metaclass=ABCMeta):
+ "An interface that each component will implement"
+
+ @staticmethod
+ @abstractmethod
+ def notify(message):
+ "The required notify method"
+
+ @staticmethod
+ @abstractmethod
+ def receive(message):
+ "The required receive method"
diff --git a/mediator/mediator.png b/mediator/mediator.png
deleted file mode 100644
index 597d426..0000000
Binary files a/mediator/mediator.png and /dev/null differ
diff --git a/mediator/mediator.py b/mediator/mediator.py
index ccba1d5..4b081f0 100644
--- a/mediator/mediator.py
+++ b/mediator/mediator.py
@@ -1,50 +1,18 @@
-from abc import ABCMeta, abstractmethod
-
-
-class IComponent(metaclass=ABCMeta):
- @staticmethod
- @abstractmethod
- def notify(msg):
- """The required notify method"""
-
- @staticmethod
- @abstractmethod
- def receive(msg):
- """The required receive method"""
-
-
-class Component(IComponent):
- def __init__(self, mediator, name):
- self.mediator = mediator
- self.name = name
-
- def notify(self, message):
- print(self.name + ": >>> Out >>> : " + message)
- self.mediator.notify(message, self)
-
- def receive(self, message):
- print(self.name + ": <<< In <<< : " + message)
+"The Subject that all components will stay synchronized with"
class Mediator():
+ "A Subject whose notify method is mediated"
+
def __init__(self):
- self.components = []
+ self._components = set()
def add(self, component):
- self.components.append(component)
-
- def notify(self, message, component):
- for _component in self.components:
- if _component != component:
- _component.receive(message)
-
-
-MEDIATOR = Mediator()
-COMPONENT1 = Component(MEDIATOR, "Component1")
-COMPONENT2 = Component(MEDIATOR, "Component2")
-COMPONENT3 = Component(MEDIATOR, "Component3")
-MEDIATOR.add(COMPONENT1)
-MEDIATOR.add(COMPONENT2)
-MEDIATOR.add(COMPONENT3)
-
-COMPONENT1.notify("data")
+ "Add components"
+ self._components.add(component)
+
+ def notify(self, message, originator):
+ "Add components except for the originator component"
+ for component in self._components:
+ if component != originator:
+ component.receive(message)
diff --git a/mediator/mediator_concept.py b/mediator/mediator_concept.py
new file mode 100644
index 0000000..61b52db
--- /dev/null
+++ b/mediator/mediator_concept.py
@@ -0,0 +1,48 @@
+# pylint: disable=too-few-public-methods
+"Mediator Concept Sample Code"
+
+
+class Mediator():
+ "The Mediator Concrete Class"
+
+ def __init__(self):
+ self.colleague1 = Colleague1()
+ self.colleague2 = Colleague2()
+
+ def colleague1_method(self):
+ "Calls the method provided by Colleague1"
+ return self.colleague1.method_1()
+
+ def colleague2_method(self):
+ "Calls the method provided by Colleague2"
+ return self.colleague2.method_2()
+
+
+class Colleague1():
+ "This Colleague provides data for Colleague2"
+
+ @staticmethod
+ def method_1():
+ "A simple method"
+ return "Here is the Colleague1 specific data you asked for"
+
+
+class Colleague2():
+ "This Colleague provides data for Colleague1"
+
+ @staticmethod
+ def method_2():
+ "A simple method"
+ return "Here is the Colleague2 specific data you asked for"
+
+
+# The Client
+MEDIATOR = Mediator()
+
+# Colleague1 wants some data from Colleague2
+DATA = MEDIATOR.colleague2_method()
+print(f"COLLEAGUE1 <--> {DATA}")
+
+# Colleague2 wants some data from Colleague1
+DATA = MEDIATOR.colleague1_method()
+print(f"COLLEAGUE2 <--> {DATA}")
diff --git a/memento/README.md b/memento/README.md
index 330bb99..98e5fe7 100644
--- a/memento/README.md
+++ b/memento/README.md
@@ -1,21 +1,154 @@
# Memento Design Pattern
-The Memento pattern is a behavioral design pattern that allows you to take a snapshot of an object's state and restore it back at a later time. It is a very common pattern to use whn implementing undo/redo functionality within your application.
+## Videos
-There 3 main objects
-Originator - (Creator) The originator is some object that has an internal state
-Caretaker - (Guardian) The caretaker is going to do something to the originator, but wants to be able to undo the change
-Memento - the copy of the state before the caretaker changed it
+Section | Video Links
+-|-
+Memento Overview |
+Memento Use Case |
+Getters/Setters |
-the Caretaker class refers to the Originator class for saving (createMemento()) and restoring (restore(memento)) originator's internal state.
-The Originator class implements
-(1) createMemento() by creating and returning a Memento object that stores originator's current internal state and
-(2) restore(memento) by restoring state from the passed in Memento object.
+## Book
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
+## Overview
-If you are undoing/redoing by executing commands on the state, that is the command pattern.
-If you are undoing/redoing by replacing state from a cache of states, that is the memento.
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
-The Client, tells the Origianetor(Object) to create a memento (copy) of itself into the mememto.
-The Client then tells the Guardian, to store a copy of the memonto.
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Memento UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+### Output
+
+``` bash
+python ./memento/memento_concept.py
+Originator: Setting state to `State #1`
+Originator: Setting state to `State #2`
+CareTaker: Getting a copy of Originators current state
+Originator: Providing Memento of state to caretaker.
+Originator: Setting state to `State #3`
+CareTaker: Getting a copy of Originators current state
+Originator: Providing Memento of state to caretaker.
+Originator: Setting state to `State #4`
+State #4
+CareTaker: Restoring Originators state from Memento
+Originator: State after restoring from Memento: `State #2`
+State #2
+CareTaker: Restoring Originators state from Memento
+Originator: State after restoring from Memento: `State #3`
+State #3
+```
+
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./memento/client.py
+Score: 200, Level: 0, Location: {'x': 0, 'y': 0, 'z': 2}
+Inventory: {'rifle', 'sword'}
+
+CareTaker: Game Save
+Score: 500, Level: 1, Location: {'x': 0, 'y': 0, 'z': 13}
+Inventory: {'motorbike', 'rifle', 'sword'}
+
+CareTaker: Game Save
+Score: 600, Level: 2, Location: {'x': 0, 'y': 0, 'z': 14}
+Inventory: {'motorbike', 'rifle', 'sword'}
+
+CareTaker: Restoring Characters attributes from Memento
+Score: 200, Level: 0, Location: {'x': 0, 'y': 0, 'z': 2}
+Inventory: {'rifle', 'sword'}
+```
+
+## New Coding Concepts
+
+### Python Getter/Setters
+
+Often when coding attributes in classes, you may want to provide methods to allow external functions to read or modify a classes internal attributes.
+
+A common approach would be to add two methods prefixed with `get_` and `set_`,
+
+``` python
+class ExampleClass:
+ def __init__(self):
+ self._value = 123
+
+ def get_value(self):
+ return self._value
+
+ def set_value(self, value):
+ self._value = value
+
+example = ExampleClass()
+print(example.get_value())
+```
+
+This makes perfect sense what the intentions are, but there is a more pythonic way of doing this and that is by using the inbuilt Python `@property` decorator.
+
+``` python
+class ExampleClass:
+ def __init__(self):
+ self._value = 123
+
+ @property
+ def value(self):
+ return self._value
+
+ @value.setter
+ def value(self, value):
+ self._value = value
+
+example = ExampleClass()
+print(example.value)
+```
+
+Note that in the above example, there is an extra decorator named `@value.setter` . This is used for setting the `_value` attribute.
+
+Along with the above two new getter/setter methods, there is also another method for deleting an attribute called `deleter` .
+
+``` python
+class ExampleClass:
+ def __init__(self):
+ self._value = 123
+
+ @property
+ def value(self):
+ return self._value
+
+ @value.setter
+ def value(self, value):
+ self._value = value
+
+ @value.deleter
+ def value(self):
+ print('Deleting _value')
+ del self._value
+
+example = ExampleClass()
+print(example.value)
+del example.value
+print(example.value) # now raises an AttributeError
+```
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/memento/caretaker.py b/memento/caretaker.py
new file mode 100644
index 0000000..5588688
--- /dev/null
+++ b/memento/caretaker.py
@@ -0,0 +1,24 @@
+"The Save/Restore Game functionality"
+
+
+class CareTaker():
+ "Guardian. Provides a narrow interface to the mementos"
+
+ def __init__(self, originator):
+ self._originator = originator
+ self._mementos = []
+
+ def save(self):
+ "Store a new Memento of the Characters current state"
+ print("CareTaker: Game Save")
+ memento = self._originator.memento
+ self._mementos.append(memento)
+
+ def restore(self, index):
+ """
+ Replace the Characters current attributes with the state
+ stored in the saved Memento
+ """
+ print("CareTaker: Restoring Characters attributes from Memento")
+ memento = self._mementos[index]
+ self._originator.memento = memento
diff --git a/memento/classes.dot b/memento/classes.dot
deleted file mode 100644
index df70678..0000000
--- a/memento/classes.dot
+++ /dev/null
@@ -1,8 +0,0 @@
-digraph "classes" {
-charset="utf-8"
-rankdir=BT
-"0" [label="{IMemento|\l|restore_memento()\lsave_memento()\l}", shape="record"];
-"1" [label="{Memento|\l|get()\l}", shape="record"];
-"2" [label="{Thing|\l|get()\lrestore_memento()\lsave_memento()\lset()\l}", shape="record"];
-"2" -> "0" [arrowhead="empty", arrowtail="none"];
-}
diff --git a/memento/client.py b/memento/client.py
new file mode 100644
index 0000000..7543b5e
--- /dev/null
+++ b/memento/client.py
@@ -0,0 +1,42 @@
+"Memento example Use Case"
+
+from game_character import GameCharacter
+from caretaker import CareTaker
+
+GAME_CHARACTER = GameCharacter()
+CARETAKER = CareTaker(GAME_CHARACTER)
+
+# start the game
+GAME_CHARACTER.register_kill()
+GAME_CHARACTER.move_forward(1)
+GAME_CHARACTER.add_inventory("sword")
+GAME_CHARACTER.register_kill()
+GAME_CHARACTER.add_inventory("rifle")
+GAME_CHARACTER.move_forward(1)
+print(GAME_CHARACTER)
+
+# save progress
+CARETAKER.save()
+
+GAME_CHARACTER.register_kill()
+GAME_CHARACTER.move_forward(1)
+GAME_CHARACTER.progress_to_next_level()
+GAME_CHARACTER.register_kill()
+GAME_CHARACTER.add_inventory("motorbike")
+GAME_CHARACTER.move_forward(10)
+GAME_CHARACTER.register_kill()
+print(GAME_CHARACTER)
+
+# save progress
+CARETAKER.save()
+GAME_CHARACTER.move_forward(1)
+GAME_CHARACTER.progress_to_next_level()
+GAME_CHARACTER.register_kill()
+print(GAME_CHARACTER)
+
+# decide you made a mistake, go back to first save
+CARETAKER.restore(0)
+print(GAME_CHARACTER)
+
+# continue
+GAME_CHARACTER.register_kill()
diff --git a/memento/game_character.py b/memento/game_character.py
new file mode 100644
index 0000000..33af916
--- /dev/null
+++ b/memento/game_character.py
@@ -0,0 +1,57 @@
+"The Game Character whose state changes"
+from memento import Memento
+
+
+class GameCharacter():
+ "The Game Character whose state changes"
+
+ def __init__(self):
+ self._score = 0
+ self._inventory = set()
+ self._level = 0
+ self._location = {"x": 0, "y": 0, "z": 0}
+
+ @property
+ def score(self):
+ "A `getter` for the objects score"
+ return self._score
+
+ def register_kill(self):
+ "The character kills its enemies as it progesses"
+ self._score += 100
+
+ def add_inventory(self, item):
+ "The character finds objects in the game"
+ self._inventory.add(item)
+
+ def progress_to_next_level(self):
+ "The characer progresses to the next level"
+ self._level += 1
+
+ def move_forward(self, amount):
+ "The character moves around the environment"
+ self._location["z"] += amount
+
+ def __str__(self):
+ return(
+ f"Score: {self._score}, "
+ f"Level: {self._level}, "
+ f"Location: {self._location}\n"
+ f"Inventory: {self._inventory}\n"
+ )
+
+ @ property
+ def memento(self):
+ "A `getter` for the characters attributes as a Memento"
+ return Memento(
+ self._score,
+ self._inventory.copy(),
+ self._level,
+ self._location.copy())
+
+ @ memento.setter
+ def memento(self, memento):
+ self._score = memento.score
+ self._inventory = memento.inventory
+ self._level = memento.level
+ self._location = memento.location
diff --git a/memento/memento.py b/memento/memento.py
index 48ce2b2..1998944 100644
--- a/memento/memento.py
+++ b/memento/memento.py
@@ -1,73 +1,11 @@
-"""
-Memento pattern example.
-"""
+"A Memento to store character attributes"
-from abc import ABCMeta, abstractmethod
-class IComponent(metaclass=ABCMeta):
- @staticmethod
- @abstractmethod
- def createMemento(msg):
- """The required notify method"""
+class Memento(): # pylint: disable=too-few-public-methods
+ "A container of characters attributes"
- @staticmethod
- @abstractmethod
- def restore(msg):
- """The required receive method"""
-
-class Memento():
- def __init__(self, state):
- self._state = state
-
- def get_state(self):
- return self._state
-
-
-class Originator(): #Creator
- _state = ""
-
- def set_state(self, state):
- print("Originator: Setting state to", state)
- self._state = state
-
- def get_state(self):
- return self._state
-
- def save_to_memento(self):
- print("Originator: Saving to Memento.")
- return Memento(self._state)
-
- def restore_from_memento(self, memento):
- self._state = memento.get_state()
- print("Originator: State after restoring from Memento:", self._state)
-
-
-class CareTaker(): #Guardian, Client
- _memento_list = []
-
- def add(self, state):
- self._memento_list.append(state)
-
- def get(self, index):
- return self._memento_list[index]
-
-
-# client
-ORIGINATOR = Originator()
-CARETAKER = CareTaker()
-
-ORIGINATOR.set_state("State #1")
-ORIGINATOR.set_state("State #2")
-CARETAKER.add(ORIGINATOR.save_to_memento())
-
-ORIGINATOR.set_state("State #3")
-CARETAKER.add(ORIGINATOR.save_to_memento())
-
-ORIGINATOR.set_state("State #4")
-print(ORIGINATOR.get_state())
-
-ORIGINATOR.restore_from_memento(CARETAKER.get(0))
-print(ORIGINATOR.get_state())
-
-ORIGINATOR.restore_from_memento(CARETAKER.get(1))
-print(ORIGINATOR.get_state())
+ def __init__(self, score, inventory, level, location):
+ self.score = score
+ self.inventory = inventory
+ self.level = level
+ self.location = location
diff --git a/memento/memento2.py b/memento/memento2.py
deleted file mode 100644
index 86f5d4e..0000000
--- a/memento/memento2.py
+++ /dev/null
@@ -1,59 +0,0 @@
-from abc import ABCMeta, abstractmethod
-
-class IMemento(metaclass=ABCMeta):
- @staticmethod
- @abstractmethod
- def save(msg):
- """The required save_memento method"""
-
- @staticmethod
- @abstractmethod
- def restore(msg):
- """The required restore_memento method"""
-
-
-class Memento():
- def __init__(self, state):
- self._state = state
-
- def get(self):
- return self._state
-
-
-class Thing(IMemento): # Creator
- _state = ""
-
- def set(self, state):
- print("Originator: Setting state to", state)
- self._state = state
-
- def get(self):
- return self._state
-
- def save(self):
- print("Saving Memento.")
- return Memento(self._state)
-
- def restore(self, memento):
- self._state = memento.get()
- print("Restoring Memento:", self._state)
-
-# client
-MEMENTOS = []
-ORIGINAL_OBJECT = Thing()
-
-ORIGINAL_OBJECT.set("State #1")
-ORIGINAL_OBJECT.set("State #2")
-MEMENTOS.append(ORIGINAL_OBJECT.save())
-
-ORIGINAL_OBJECT.set("State #3")
-MEMENTOS.append(ORIGINAL_OBJECT.save())
-
-ORIGINAL_OBJECT.set("State #4")
-print(ORIGINAL_OBJECT.get())
-
-ORIGINAL_OBJECT.restore(MEMENTOS[0])
-print(ORIGINAL_OBJECT.get())
-
-ORIGINAL_OBJECT.restore(MEMENTOS[1])
-print(ORIGINAL_OBJECT.get())
diff --git a/memento/memento_concept.py b/memento/memento_concept.py
new file mode 100644
index 0000000..e458c5f
--- /dev/null
+++ b/memento/memento_concept.py
@@ -0,0 +1,89 @@
+"Memento pattern concept"
+
+
+class Memento(): # pylint: disable=too-few-public-methods
+ "A container of state"
+
+ def __init__(self, state):
+ self.state = state
+
+
+class Originator():
+ "The Object in the application whose state changes"
+
+ def __init__(self):
+ self._state = ""
+
+ @property
+ def state(self):
+ "A `getter` for the objects state"
+ return self._state
+
+ @state.setter
+ def state(self, state):
+ print(f"Originator: Setting state to `{state}`")
+ self._state = state
+
+ @property
+ def memento(self):
+ "A `getter` for the objects state but packaged as a Memento"
+ print("Originator: Providing Memento of state to caretaker.")
+ return Memento(self._state)
+
+ @memento.setter
+ def memento(self, memento):
+ self._state = memento.state
+ print(
+ f"Originator: State after restoring from Memento: "
+ f"`{self._state}`")
+
+
+class CareTaker():
+ "Guardian. Provides a narrow interface to the mementos"
+
+ def __init__(self, originator):
+ self._originator = originator
+ self._mementos = []
+
+ def create(self):
+ "Store a new Memento of the Originators current state"
+ print("CareTaker: Getting a copy of Originators current state")
+ memento = self._originator.memento
+ self._mementos.append(memento)
+
+ def restore(self, index):
+ """
+ Replace the Originators current state with the state
+ stored in the saved Memento
+ """
+ print("CareTaker: Restoring Originators state from Memento")
+ memento = self._mementos[index]
+ self._originator.memento = memento
+
+
+# The Client
+ORIGINATOR = Originator()
+CARETAKER = CareTaker(ORIGINATOR)
+
+# originators state can change periodically due to application events
+ORIGINATOR.state = "State #1"
+ORIGINATOR.state = "State #2"
+
+# lets backup the originators
+CARETAKER.create()
+
+# more changes, and then another backup
+ORIGINATOR.state = "State #3"
+CARETAKER.create()
+
+# more changes
+ORIGINATOR.state = "State #4"
+print(ORIGINATOR.state)
+
+# restore from first backup
+CARETAKER.restore(0)
+print(ORIGINATOR.state)
+
+# restore from second backup
+CARETAKER.restore(1)
+print(ORIGINATOR.state)
diff --git a/mkdocs.yml b/mkdocs.yml
deleted file mode 100644
index 2bb755d..0000000
--- a/mkdocs.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-site_name: Design Patterns In Python
-nav:
- - Home: index.md
- #- Creational:
- - Factory: factory.md
- - Abstract Factory: abstract_factory.md
- - Builder: builder.md
- - ProtoType: prototype.md
- #- Structural:
- - Decorator: decorator.md
- - Adapter: adapter.md
- - Facade: facade.md
- - Composite: composite.md
- - Proxy: proxy.md
- #- Behavioural:
- - Command: command.md
- - Command Undo/Redo: command_undo_redo.md
- - Chain of Responsibility: chain_of_responsibility.md
- - Observer: observer.md
- - Iterator: iterator.md
- - Mediator: mediator.md
-theme:
- name: 'material'
- custom_dir: 'G:\mkdocs-material\material'
- language: 'en'
- palette:
- primary: 'Blue Grey'
- accent: 'Blue Grey'
- font:
- false
-
-markdown_extensions:
- - codehilite:
- guess_lang: false
-google_analytics:
- - 'UA-147973042-3'
- - 'auto'
-site_author: 'Sean Bradley'
-copyright: 'Copyright 2019 © Sean Bradley'
diff --git a/notes b/notes
deleted file mode 100644
index b1a57ad..0000000
--- a/notes
+++ /dev/null
@@ -1,5 +0,0 @@
-Adapter V Facade V Proxy V Decorator
-• Adapter adapts an existing class/object to a new interface. Make incompatible interface compatible.
-• Facade is a class that simplifies a complicated set of functionality.
-• Proxy provides the same interface as the subject class and allows additional functionality to be added.
-• Decorator is a wrapper to extend functionality to an existing class at runtime.
\ No newline at end of file
diff --git a/observer/Observer Design Pattern.md b/observer/Observer Design Pattern.md
deleted file mode 100644
index 6e79515..0000000
--- a/observer/Observer Design Pattern.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# Observer Pattern
-
-The observer pattern is a software design pattern in which an object, called the subject or observable, manages a list of dependents, called observers, and notifies them automatically of any internal state changes, and calls one of their methods.
-
-
-
diff --git a/observer/Observer Design Pattern.pdf b/observer/Observer Design Pattern.pdf
deleted file mode 100644
index e8900a8..0000000
Binary files a/observer/Observer Design Pattern.pdf and /dev/null differ
diff --git a/observer/README.md b/observer/README.md
index 6e79515..1965ab3 100644
--- a/observer/README.md
+++ b/observer/README.md
@@ -1,6 +1,91 @@
# Observer Pattern
-The observer pattern is a software design pattern in which an object, called the subject or observable, manages a list of dependents, called observers, and notifies them automatically of any internal state changes, and calls one of their methods.
+## Videos
-
+Section | Video Links
+-|-
+Observer Overview |
+Observer Use Case |
+Python **Set** |
+## Book
+
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
+
+## Overview
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Observer UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Output
+
+``` bash
+python ./observer/observer_concept.py
+Observer id:2084220160272 received ('First Notification', [1, 2, 3])
+Observer id:2084220160224 received ('First Notification', [1, 2, 3])
+Observer id:2084220160272 received ('Second Notification', {'A': 1, 'B': 2, 'C': 3})
+```
+
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./observer/client.py
+PieGraph, id:1
+Drawing a Pie graph using data:[1, 2, 3]
+BarGraph, id:2
+Drawing a Bar graph using data:[1, 2, 3]
+TableView, id:3
+Drawing a Table view using data:[1, 2, 3]
+PieGraph, id:1
+Drawing a Pie graph using data:[4, 5, 6]
+TableView, id:3
+Drawing a Table view using data:[4, 5, 6]
+```
+
+## New Coding Concepts
+
+### Python Set
+
+A Python **Set** is similar to a List. Except that the items in the Set are guaranteed to be unique, even if you try to add a duplicate. A set is a good choice for keeping a collection of observables, since the problem of duplicate observables is automatically handled.
+
+A Set can be instantiated using the curly braces `{}` or `set()`, verses `[]` for a [List](/builder#python-list) and `()` for a [Tuple](/bridge#python-tuple). It is not the same as a [Dictionary](/singleton#python-dictionary), which also uses `{}`, since the dictionary items are created as `key:value` pairs. ie `{"a": 1, "b": 2, "c": 3}`
+
+``` python
+PS> python
+>>> items = {"yankee", "doodle", "dandy", "doodle"}
+>>> items
+{'yankee', 'doodle', 'dandy'}
+>>> items.add("grandy")
+>>> items
+{'grandy', 'yankee', 'doodle', 'dandy'}
+>>> items.remove("doodle")
+>>> items
+{'grandy', 'yankee', 'dandy'}
+```
+
+Note, if instantiating an empty **Set** then use `my_object = Set()` rather than `my_object = {}` to reduce ambiguity with creating an empty [Dictionary](/singleton#python-dictionary).
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/observer/bar_graph_view.py b/observer/bar_graph_view.py
new file mode 100644
index 0000000..263129e
--- /dev/null
+++ b/observer/bar_graph_view.py
@@ -0,0 +1,20 @@
+"An observer"
+from interface_data_view import IDataView
+
+
+class BarGraphView(IDataView):
+ "The concrete observer"
+
+ def __init__(self, observable):
+ self._observable = observable
+ self._id = self._observable.subscribe(self)
+
+ def notify(self, data):
+ print(f"BarGraph, id:{self._id}")
+ self.draw(data)
+
+ def draw(self, data):
+ print(f"Drawing a Bar graph using data:{data}")
+
+ def delete(self):
+ self._observable.unsubscribe(self._id)
diff --git a/observer/classes.dot b/observer/classes.dot
deleted file mode 100644
index a1a6cb9..0000000
--- a/observer/classes.dot
+++ /dev/null
@@ -1,10 +0,0 @@
-digraph "classes" {
-charset="utf-8"
-rankdir=BT
-"0" [label="{IObservable|\l|notify()\lsubscribe()\lunsubscribe()\l}", shape="record"];
-"1" [label="{IObserver|\l|notify()\l}", shape="record"];
-"2" [label="{Observer|\l|notify()\l}", shape="record"];
-"3" [label="{Subject|\l|notify()\lsubscribe()\lunsubscribe()\l}", shape="record"];
-"2" -> "1" [arrowhead="empty", arrowtail="none"];
-"3" -> "0" [arrowhead="empty", arrowtail="none"];
-}
diff --git a/observer/client.py b/observer/client.py
new file mode 100644
index 0000000..5392c60
--- /dev/null
+++ b/observer/client.py
@@ -0,0 +1,28 @@
+"Observer Design Pattern Concept"
+
+from data_model import DataModel
+from data_controller import DataController
+from pie_graph_view import PieGraphView
+from bar_graph_view import BarGraphView
+from table_view import TableView
+
+# A local data view that the hypothetical external controller updates
+DATA_MODEL = DataModel()
+
+# Add some visualisation that use the dataview
+PIE_GRAPH_VIEW = PieGraphView(DATA_MODEL)
+BAR_GRAPH_VIEW = BarGraphView(DATA_MODEL)
+TABLE_VIEW = TableView(DATA_MODEL)
+
+
+# A hypothetical data controller running in a different process
+DATA_CONTROLLER = DataController()
+
+# The hypothetical external data controller updates some data
+DATA_CONTROLLER.notify([1, 2, 3])
+
+# Client now removes a local BAR_GRAPH
+BAR_GRAPH_VIEW.delete()
+
+# The hypothetical external data controller updates the data again
+DATA_CONTROLLER.notify([4, 5, 6])
diff --git a/observer/data_controller.py b/observer/data_controller.py
new file mode 100644
index 0000000..8661b34
--- /dev/null
+++ b/observer/data_controller.py
@@ -0,0 +1,24 @@
+"A Data Conroller that is a Subject"
+from interface_data_controller import IDataController
+
+
+class DataController(IDataController):
+ "A Subject (a.k.a Observable)"
+
+ _observers = set()
+
+ def __new__(cls):
+ return cls
+
+ @classmethod
+ def subscribe(cls, observer):
+ cls._observers.add(observer)
+
+ @classmethod
+ def unsubscribe(cls, observer):
+ cls._observers.remove(observer)
+
+ @classmethod
+ def notify(cls, *args):
+ for observer in cls._observers:
+ observer.notify(*args)
diff --git a/observer/data_model.py b/observer/data_model.py
new file mode 100644
index 0000000..9af6cb1
--- /dev/null
+++ b/observer/data_model.py
@@ -0,0 +1,26 @@
+"A Data Model that observes the Data Controller"
+from interface_data_model import IDataModel
+from data_controller import DataController
+
+
+class DataModel(IDataModel):
+ "A Subject (a.k.a Observable)"
+
+ def __init__(self):
+ self._observers = {}
+ self._counter = 0
+ # subscribing to an external hypothetical data controller
+ self._data_controller = DataController()
+ self._data_controller.subscribe(self)
+
+ def subscribe(self, observer):
+ self._counter = self._counter + 1
+ self._observers[self._counter] = observer
+ return self._counter
+
+ def unsubscribe(self, observer_id):
+ self._observers.pop(observer_id)
+
+ def notify(self, data):
+ for observer in self._observers:
+ self._observers[observer].notify(data)
diff --git a/observer/interface_data_controller.py b/observer/interface_data_controller.py
new file mode 100644
index 0000000..cf650b2
--- /dev/null
+++ b/observer/interface_data_controller.py
@@ -0,0 +1,20 @@
+"A Data Controller Interface"
+from abc import ABCMeta, abstractmethod
+
+
+class IDataController(metaclass=ABCMeta):
+ "A Subject Interface"
+ @staticmethod
+ @abstractmethod
+ def subscribe(observer):
+ "The subscribe method"
+
+ @staticmethod
+ @abstractmethod
+ def unsubscribe(observer):
+ "The unsubscribe method"
+
+ @staticmethod
+ @abstractmethod
+ def notify(observer):
+ "The notify method"
diff --git a/observer/interface_data_model.py b/observer/interface_data_model.py
new file mode 100644
index 0000000..105baeb
--- /dev/null
+++ b/observer/interface_data_model.py
@@ -0,0 +1,21 @@
+"A Data Model Interface"
+from abc import ABCMeta, abstractmethod
+
+
+class IDataModel(metaclass=ABCMeta):
+ "A Subject Interface"
+
+ @staticmethod
+ @abstractmethod
+ def subscribe(observer):
+ "The subscribe method"
+
+ @staticmethod
+ @abstractmethod
+ def unsubscribe(observer_id):
+ "The unsubscribe method"
+
+ @staticmethod
+ @abstractmethod
+ def notify(data):
+ "The notify method"
diff --git a/observer/interface_data_view.py b/observer/interface_data_view.py
new file mode 100644
index 0000000..3016064
--- /dev/null
+++ b/observer/interface_data_view.py
@@ -0,0 +1,21 @@
+"The Data View interface"
+from abc import ABCMeta, abstractmethod
+
+
+class IDataView(metaclass=ABCMeta):
+ "A method for the Observer to implement"
+
+ @staticmethod
+ @abstractmethod
+ def notify(data):
+ "Receive notifications"
+
+ @staticmethod
+ @abstractmethod
+ def draw(data):
+ "Draw the view"
+
+ @staticmethod
+ @abstractmethod
+ def delete():
+ "a delete method to remove observer specific resources"
diff --git a/observer/observer.dot b/observer/observer.dot
deleted file mode 100644
index 3c8cf54..0000000
--- a/observer/observer.dot
+++ /dev/null
@@ -1,13 +0,0 @@
-digraph "classes" {
-charset="utf-8"
-nodesep=1
-rankdir=BT
-{rank=same;2,3}
-"0" [label="{IObservable|\l|notify()\lsubscribe()\lunsubscribe()\l}", shape="record"];
-"1" [label="{IObserver|\l|notify()\l}", shape="record"];
-"2" [label="{Observer|\l|notify()\l}", shape="record"];
-"3" [label="{Subject|\l|notify()\lsubscribe()\lunsubscribe()\l}", shape="record"];
-"2" -> "1" [arrowhead="empty", arrowtail="none"];
-"3" -> "0" [arrowhead="empty", arrowtail="none"];
-"2" -> "3" [arrowhead="ediamond", arrowtail="vee", dir="both"];
-}
diff --git a/observer/observer.png b/observer/observer.png
deleted file mode 100644
index 595ce02..0000000
Binary files a/observer/observer.png and /dev/null differ
diff --git a/observer/observer.py b/observer/observer.py
deleted file mode 100644
index 149c987..0000000
--- a/observer/observer.py
+++ /dev/null
@@ -1,62 +0,0 @@
-"""
-Observer Design Pattern
-"""
-
-from abc import ABCMeta, abstractmethod
-
-
-class IObservable(metaclass=ABCMeta):
- @staticmethod
- @abstractmethod
- def subscribe(observer):
- """The subscribe method"""
-
- @staticmethod
- @abstractmethod
- def unsubscribe(observer):
- """The unsubscribe method"""
-
- @staticmethod
- @abstractmethod
- def notify(observer):
- """The notify method"""
-
-
-class Subject(IObservable):
- def __init__(self):
- self._observers = set()
-
- def subscribe(self, observer):
- self._observers.add(observer)
-
- def unsubscribe(self, observer):
- self._observers.remove(observer)
-
- def notify(self, *args, **kwargs):
- for observer in self._observers:
- observer.notify(self, *args, **kwargs)
-
-
-class IObserver(metaclass=ABCMeta):
- @staticmethod
- @abstractmethod
- def notify(observable, *args, **kwargs):
- """Receive notifications"""
-
-
-class Observer(IObserver):
- def __init__(self, observable):
- observable.subscribe(self)
-
- def notify(self, observable, *args, **kwargs):
- print("Observer received", args, kwargs)
-
-
-SUBJECT = Subject()
-OBSERVERA = Observer(SUBJECT)
-OBSERVERB = Observer(SUBJECT)
-
-SUBJECT.notify("Hello Observers")
-
-SUBJECT.unsubscribe(OBSERVERB)
-SUBJECT.notify("Hello Observers")
diff --git a/observer/observer_concept.py b/observer/observer_concept.py
new file mode 100644
index 0000000..1c29aa3
--- /dev/null
+++ b/observer/observer_concept.py
@@ -0,0 +1,71 @@
+# pylint: disable=too-few-public-methods
+# pylint: disable=arguments-differ
+"Observer Design Pattern Concept"
+
+from abc import ABCMeta, abstractmethod
+
+
+class IObservable(metaclass=ABCMeta):
+ "The Subject Interface"
+
+ @staticmethod
+ @abstractmethod
+ def subscribe(observer):
+ "The subscribe method"
+
+ @staticmethod
+ @abstractmethod
+ def unsubscribe(observer):
+ "The unsubscribe method"
+
+ @staticmethod
+ @abstractmethod
+ def notify(observer):
+ "The notify method"
+
+
+class Subject(IObservable):
+ "The Subject (a.k.a Observable)"
+
+ def __init__(self):
+ self._observers = set()
+
+ def subscribe(self, observer):
+ self._observers.add(observer)
+
+ def unsubscribe(self, observer):
+ self._observers.remove(observer)
+
+ def notify(self, *args):
+ for observer in self._observers:
+ observer.notify(*args)
+
+
+class IObserver(metaclass=ABCMeta):
+ "A method for the Observer to implement"
+
+ @staticmethod
+ @abstractmethod
+ def notify(observable, *args):
+ "Receive notifications"
+
+
+class Observer(IObserver):
+ "The concrete observer"
+
+ def __init__(self, observable):
+ observable.subscribe(self)
+
+ def notify(self, *args):
+ print(f"Observer id:{id(self)} received {args}")
+
+
+# The Client
+SUBJECT = Subject()
+OBSERVER_A = Observer(SUBJECT)
+OBSERVER_B = Observer(SUBJECT)
+
+SUBJECT.notify("First Notification", [1, 2, 3])
+
+SUBJECT.unsubscribe(OBSERVER_B)
+SUBJECT.notify("Second Notification", {"A": 1, "B": 2, "C": 3})
diff --git a/observer/pie_graph_view.py b/observer/pie_graph_view.py
new file mode 100644
index 0000000..5980b63
--- /dev/null
+++ b/observer/pie_graph_view.py
@@ -0,0 +1,20 @@
+"An observer"
+from interface_data_view import IDataView
+
+
+class PieGraphView(IDataView):
+ "The concrete observer"
+
+ def __init__(self, observable):
+ self._observable = observable
+ self._id = self._observable.subscribe(self)
+
+ def notify(self, data):
+ print(f"PieGraph, id:{self._id}")
+ self.draw(data)
+
+ def draw(self, data):
+ print(f"Drawing a Pie graph using data:{data}")
+
+ def delete(self):
+ self._observable.unsubscribe(self._id)
diff --git a/observer/table_view.py b/observer/table_view.py
new file mode 100644
index 0000000..23fb8d5
--- /dev/null
+++ b/observer/table_view.py
@@ -0,0 +1,20 @@
+"An observer"
+from interface_data_view import IDataView
+
+
+class TableView(IDataView):
+ "The concrete observer"
+
+ def __init__(self, observable):
+ self._observable = observable
+ self._id = self._observable.subscribe(self)
+
+ def notify(self, data):
+ print(f"TableView, id:{self._id}")
+ self.draw(data)
+
+ def draw(self, data):
+ print(f"Drawing a Table view using data:{data}")
+
+ def delete(self):
+ self._observable.unsubscribe(self._id)
diff --git a/observer/weather_observer.dot b/observer/weather_observer.dot
deleted file mode 100644
index e0c98ef..0000000
--- a/observer/weather_observer.dot
+++ /dev/null
@@ -1,15 +0,0 @@
-digraph "classes" {
-charset="utf-8"
-nodesep=0.5
-ranksep=1
-{rank=same;3;8}
-"0" [label="{ABCWeather|\l|notify()\l}", shape="record"];
-"1" [label="{BBCWeather|\l|notify()\l}", shape="record"];
-"3" [label="{IObserver|\l|notify()\l}", shape="record"];
-"4" [label="{NBCWeather|\l|notify()\l}", shape="record"];
-"8" [label="{Weather|\l|subscribe()\lunsubscribe()\lnotify()\l}", shape="record"];
-"0" -> "3" [arrowhead="empty", arrowtail="none"];
-"1" -> "3" [arrowhead="empty", arrowtail="none"];
-"4" -> "3" [arrowhead="empty", arrowtail="none"];
-"3" -> "8" [arrowhead="ediamond", arrowtail="vee", dir="both"];
-}
diff --git a/observer/weather_observer.png b/observer/weather_observer.png
deleted file mode 100644
index be35082..0000000
Binary files a/observer/weather_observer.png and /dev/null differ
diff --git a/observer/weather_observer.py b/observer/weather_observer.py
deleted file mode 100644
index f9c12c0..0000000
--- a/observer/weather_observer.py
+++ /dev/null
@@ -1,81 +0,0 @@
-"""
-Observer Design Pattern
-"""
-
-from abc import ABCMeta, abstractmethod
-from enum import Enum
-import time
-
-
-class WeatherType(Enum):
- SUNNY = 1
- RAINY = 2
- WINDY = 3
- COLD = 4
-
-
-class Weather:
- """Observable"""
-
- def __init__(self):
- self._observers = set()
-
- def subscribe(self, observer):
- self._observers.add(observer)
-
- def unsubscribe(self, observer):
- self._observers.remove(observer)
-
- def notify(self, weather_type):
- for observer in self._observers:
- observer.notify(weather_type)
-
-
-class IObserver(metaclass=ABCMeta):
- @staticmethod
- @abstractmethod
- def notify(weather_type):
- """Update all the subscribed observers"""
-
-
-class BBCWeather(IObserver):
- def __init__(self, observable):
- observable.subscribe(self)
-
- def notify(self, weather_type):
- print(f"{__class__} : {repr(weather_type)}")
-
-
-class ABCWeather(IObserver):
- def __init__(self, observable):
- observable.subscribe(self)
-
- def notify(self, weather_type):
- print(f"{__class__} : {repr(weather_type)}")
-
-
-class NBCWeather(IObserver):
- def __init__(self, observable):
- observable.subscribe(self)
-
- def notify(self, weather_type):
- print(f"{__class__} : {repr(weather_type)}")
-
-
-if __name__ == "__main__":
-
- WEATHERSERVICE = Weather()
-
- BBCWEATHER = BBCWeather(WEATHERSERVICE)
- ABCWEATHER = ABCWeather(WEATHERSERVICE)
- NBCWEATHER = NBCWeather(WEATHERSERVICE)
-
- WEATHERSERVICE.notify(WeatherType.RAINY)
- WEATHERSERVICE.unsubscribe(BBCWEATHER)
- time.sleep(2)
- WEATHERSERVICE.notify(WeatherType.SUNNY)
- WEATHERSERVICE.unsubscribe(NBCWEATHER)
- time.sleep(2)
- WEATHERSERVICE.notify(WeatherType.WINDY)
- time.sleep(2)
- WEATHERSERVICE.notify(WeatherType.COLD)
diff --git a/prototype/Prototype Design Pattern.md b/prototype/Prototype Design Pattern.md
deleted file mode 100644
index 4e8de9a..0000000
--- a/prototype/Prototype Design Pattern.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# Prototype Design Pattern
-
-Prototype design pattern is good for when creating a new objects may require more resources than you want to use or have available, versus just making a new copy in memory.
-Eg, A file you've downloaded from a server may be large, but since it is already in memory, you could just clone it, and work on the new copy independently of the original.
-
-In the prototype patterns interface, you create a static clone method that should be implemented by all classes that use the interface.
-How the clone method is implemented in the concrete class is up to you.
-You will need to decide whether a shallow or deep copy is required.
-
-* A shallow copy, copies and creates new references 1 level deep,
-* A deep copy, copies and creates new references for all levels.
-
-In Python you have mutable objects such as Lists, Dictionaries, Sets and any custom Objects you have created. A shallow copy, will create new copies of the objects with new references in memory, but the underlying data will point to the same location as the original copy. Be sure to test your implementation that
-the copy method you use works as you expect.
-
-
-
diff --git a/prototype/Prototype Design Pattern.pdf b/prototype/Prototype Design Pattern.pdf
deleted file mode 100644
index d1790da..0000000
Binary files a/prototype/Prototype Design Pattern.pdf and /dev/null differ
diff --git a/prototype/README.md b/prototype/README.md
index 4e8de9a..0b69ff8 100644
--- a/prototype/README.md
+++ b/prototype/README.md
@@ -1,17 +1,35 @@
# Prototype Design Pattern
-Prototype design pattern is good for when creating a new objects may require more resources than you want to use or have available, versus just making a new copy in memory.
-Eg, A file you've downloaded from a server may be large, but since it is already in memory, you could just clone it, and work on the new copy independently of the original.
+## Videos
-In the prototype patterns interface, you create a static clone method that should be implemented by all classes that use the interface.
-How the clone method is implemented in the concrete class is up to you.
-You will need to decide whether a shallow or deep copy is required.
+Section | Video Links
+-|-
+Prototype Overview |
+Prototype Use Case |
+Python **id()** function |
-* A shallow copy, copies and creates new references 1 level deep,
-* A deep copy, copies and creates new references for all levels.
+## Book
-In Python you have mutable objects such as Lists, Dictionaries, Sets and any custom Objects you have created. A shallow copy, will create new copies of the objects with new references in memory, but the underlying data will point to the same location as the original copy. Be sure to test your implementation that
-the copy method you use works as you expect.
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
-
+## Overview
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Prototype UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/prototype/classes_prototype.dot b/prototype/classes_prototype.dot
deleted file mode 100644
index ae03e0f..0000000
--- a/prototype/classes_prototype.dot
+++ /dev/null
@@ -1,9 +0,0 @@
-digraph "classes_prototype" {
-charset="utf-8"
-rankdir=BT
-"0" [label="{ConcreteClass1|d : dict\li : int\ll : list\ls : str\l|clone()\l}", shape="record"];
-"1" [label="{ConcreteClass2|d : dict\li : int\ll : list\ls : str\l|clone()\l}", shape="record"];
-"2" [label="{IProtoType|\l|clone()\l}", shape="record"];
-"0" -> "2" [arrowhead="empty", arrowtail="none"];
-"1" -> "2" [arrowhead="empty", arrowtail="none"];
-}
diff --git a/prototype/client.py b/prototype/client.py
new file mode 100644
index 0000000..502795b
--- /dev/null
+++ b/prototype/client.py
@@ -0,0 +1,43 @@
+"Prototype Use Case Example Code"
+from document import Document
+
+# Creating a document containing a list of two lists
+ORIGINAL_DOCUMENT = Document("Original", [[1, 2, 3, 4], [5, 6, 7, 8]])
+print(ORIGINAL_DOCUMENT)
+print()
+
+DOCUMENT_COPY_1 = ORIGINAL_DOCUMENT.clone(1) # shallow copy
+DOCUMENT_COPY_1.name = "Copy 1"
+# This also modified ORIGINAL_DOCUMENT because of the shallow copy
+# when using mode 1
+DOCUMENT_COPY_1.list[1][2] = 200
+print(DOCUMENT_COPY_1)
+print(ORIGINAL_DOCUMENT)
+print()
+
+DOCUMENT_COPY_2 = ORIGINAL_DOCUMENT.clone(2) # 2 level shallow copy
+DOCUMENT_COPY_2.name = "Copy 2"
+# This does NOT modify ORIGINAL_DOCUMENT because it changes the
+# list[1] reference that was deep copied when using mode 2
+DOCUMENT_COPY_2.list[1] = [9, 10, 11, 12]
+print(DOCUMENT_COPY_2)
+print(ORIGINAL_DOCUMENT)
+print()
+
+DOCUMENT_COPY_3 = ORIGINAL_DOCUMENT.clone(2) # 2 level shallow copy
+DOCUMENT_COPY_3.name = "Copy 3"
+# This does modify ORIGINAL_DOCUMENT because it changes the element of
+# list[1][0] that was NOT deep copied recursively when using mode 2
+DOCUMENT_COPY_3.list[1][0] = "1234"
+print(DOCUMENT_COPY_3)
+print(ORIGINAL_DOCUMENT)
+print()
+
+DOCUMENT_COPY_4 = ORIGINAL_DOCUMENT.clone(3) # deep copy (recursive)
+DOCUMENT_COPY_4.name = "Copy 4"
+# This does NOT modify ORIGINAL_DOCUMENT because it
+# deep copies all levels recursively when using mode 3
+DOCUMENT_COPY_4.list[1][0] = "5678"
+print(DOCUMENT_COPY_4)
+print(ORIGINAL_DOCUMENT)
+print()
diff --git a/prototype/document.py b/prototype/document.py
new file mode 100644
index 0000000..9372d1b
--- /dev/null
+++ b/prototype/document.py
@@ -0,0 +1,35 @@
+"A sample document to be used in the Prototype example"
+import copy # a python library useful for deep copying
+from interface_prototype import IProtoType
+
+
+class Document(IProtoType):
+ "A Concrete Class"
+
+ def __init__(self, name, l):
+ self.name = name
+ self.list = l
+
+ def clone(self, mode):
+ " This clone method uses different copy techniques "
+ if mode == 1:
+ # results in a 1 level shallow copy of the Document
+ doc_list = self.list
+ if mode == 2:
+ # results in a 2 level shallow copy of the Document
+ # since it also create new references for the 1st level list
+ # elements aswell
+ doc_list = self.list.copy()
+ if mode == 3:
+ # recursive deep copy. Slower but results in a new copy
+ # where no sub elements are shared by reference
+ doc_list = copy.deepcopy(self.list)
+
+ return type(self)(
+ self.name, # a shallow copy is returned of the name property
+ doc_list # copy method decided by mode argument
+ )
+
+ def __str__(self):
+ " Overriding the default __str__ method for our object."
+ return f"{id(self)}\tname={self.name}\tlist={self.list}"
diff --git a/prototype/interface_prototype.py b/prototype/interface_prototype.py
new file mode 100644
index 0000000..18f646f
--- /dev/null
+++ b/prototype/interface_prototype.py
@@ -0,0 +1,13 @@
+# pylint: disable=too-few-public-methods
+"Prototype Concept Sample Code"
+from abc import ABCMeta, abstractmethod
+
+
+class IProtoType(metaclass=ABCMeta):
+ "interface with clone method"
+ @staticmethod
+ @abstractmethod
+ def clone(mode):
+ """The clone, deep or shallow.
+ It is up to you how you want to implement
+ the details in your concrete class"""
diff --git a/prototype/prototype.png b/prototype/prototype.png
deleted file mode 100644
index 209f77c..0000000
Binary files a/prototype/prototype.png and /dev/null differ
diff --git a/prototype/prototype.py b/prototype/prototype.py
deleted file mode 100644
index 602f9a5..0000000
--- a/prototype/prototype.py
+++ /dev/null
@@ -1,89 +0,0 @@
-"""
-Prototype design pattern is good for when creating a new object
-may require more resources than you want to use or have available,
-versus just making a new copy in memory.
-Eg, A file you've downloaded from a server may be large, but
-since it is already in memory, you could just clone it, and
-work on the new copy independently of the original.
-
-In the prototype patterns interface, you create a static clone method that
-should be implemented by all classes that use the interface.
-How the clone method is implemented in the concrete class is up to you.
-You will need to decide whether a shallow or deep copy is required.
-A shallow copy, copies and creates new references 1 level deep,
-A deep copy, copies and creates new references for all levels.
-In Python you have mutable objects such as Lists, Dictionaries,
-Sets and any custom Objects you have created. A shallow copy,
-will create new copies of the objects with new references in memory,
-but the underlying data will point to the same location as the original
-copy. Be sure to test your implementation that
-the copy method you use works as expected.
-"""
-
-from abc import ABCMeta, abstractstaticmethod
-import copy
-
-
-class IProtoType(metaclass=ABCMeta):
- """interface with clone method"""
- @abstractstaticmethod
- def clone():
- """The clone, deep or shallow, is up to how you
- want implement the details in your concrete class?"""
-
-
-class ConcreteClass1(IProtoType):
- """concrete class 1"""
-
- def __init__(self, i=0, s="", l=[], d={}):
- self.i = i
- self.s = s
- self.l = l
- self.d = d
-
- def clone(self):
- return type(self)(
- self.i,
- self.s,
- self.l.copy(),
- self.d.copy())
-
- def __str__(self):
- return f"{id(self)}\ti={self.i}\ts={self.s}\tl={self.l}\td={self.d}"
-
-
-class ConcreteClass2(IProtoType):
- """concrete class 2"""
-
- def __init__(self, i=0, s="", l=[], d={}):
- self.i = i
- self.s = s
- self.l = l
- self.d = d
-
- def clone(self):
- return type(self)(
- self.i,
- self.s,
- copy.deepcopy(self.l),
- copy.deepcopy(self.d))
-
- def __str__(self):
- return f"i={self.i}\t\ts={self.s}\tl={self.l}\td={self.d}\n{id(self.i)}\t{id(self.s)}\t{id(self.l)}\t{id(self.d)}\t"
-
-
-if __name__ == "__main__":
-
- OBJECT1 = ConcreteClass1(
- 1,
- "OBJECT1",
- [1, 2, 3],
- {"a": 4, "b": 5, "c": 6}
- )
- print(f"OBJECT1 {OBJECT1}")
-
- OBJECT2 = OBJECT1.clone()
- OBJECT2.s = "OBJECT2"
- OBJECT2.l[0] = 10
- print(f"OBJECT2 {OBJECT2}")
- print(f"OBJECT1 {OBJECT1}")
diff --git a/prototype/prototype_concept.py b/prototype/prototype_concept.py
new file mode 100644
index 0000000..3c82b1b
--- /dev/null
+++ b/prototype/prototype_concept.py
@@ -0,0 +1,57 @@
+# pylint: disable=too-few-public-methods
+# pylint: disable=arguments-differ
+"Prototype Concept Sample Code"
+from abc import ABCMeta, abstractmethod
+
+
+class IProtoType(metaclass=ABCMeta):
+ "interface with clone method"
+ @staticmethod
+ @abstractmethod
+ def clone():
+ """The clone, deep or shallow.
+ It is up to you how you want to implement
+ the details in your concrete class"""
+
+
+class MyClass(IProtoType):
+ "A Concrete Class"
+
+ def __init__(self, field):
+ self.field = field # any value of any type
+
+ def clone(self):
+ " This clone method uses a shallow copy technique "
+ return type(self)(
+ self.field # a shallow copy is returned
+ # self.field.copy() # this is also a shallow copy, but has
+ # also shallow copied the first level of the field. So it
+ # is essentially a shallow copy but 2 levels deep. To
+ # recursively deep copy collections containing inner
+ # collections,
+ # eg lists of lists,
+ # Use https://docs.python.org/3/library/copy.html instead.
+ # See example below.
+ )
+
+ def __str__(self):
+ return f"{id(self)}\tfield={self.field}\ttype={type(self.field)}"
+
+
+# The Client
+OBJECT1 = MyClass([1, 2, 3, 4]) # Create the object containing a list
+print(f"OBJECT1 {OBJECT1}")
+
+OBJECT2 = OBJECT1.clone() # Clone
+
+# Change the value of one of the list elements in OBJECT2,
+# to see if it also modifies the list element in OBJECT1.
+# If it changed OBJECT1s copy also, then the clone was done
+# using a 1 level shallow copy process.
+# Modify the clone method above to try a 2 level shallow copy instead
+# and compare the output
+OBJECT2.field[1] = 101
+
+# Comparing OBJECT1 and OBJECT2
+print(f"OBJECT2 {OBJECT2}")
+print(f"OBJECT1 {OBJECT1}")
diff --git a/proxy/README.md b/proxy/README.md
index 12f1f95..64456cf 100644
--- a/proxy/README.md
+++ b/proxy/README.md
@@ -1,19 +1,104 @@
# Proxy Design Pattern
-The proxy design pattern is a class functioning as an interface to another class or object.
+## Videos
-A proxy could be for anything, such as a network connection, an object in memory, a file, or anything else you need to provide an abstraction between.
+Section | Video Links
+-|-
+Proxy Overview |
+Proxy Use Case |
+**\_\_class\_\_** Attribute |
+Circular Imports |
-It is a wrapper called by a client to access the real underlying object.
+## Book
-Additional functionality can be provided at in the proxy abstraction if required.
-eg, caching, authorization, validation, lazy initialization, logging.
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
-The proxy should implement the subject interface as much as practicable so that the proxy and subject appear identical to the client.
+## Overview
-The Proxy Pattern may occasionally also be referred to as Monkey Patching or
-Object Augmentation
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
-
+## Terminology
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+## Proxy UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./proxy/proxy_concept.py
+1848118706080
+pulling data from RealSubject
+[1, 2, 3]
+pulling data from Proxy cache
+[1, 2, 3]
+```
+
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./proxy/client.py
+I am the form of a Lion
+I am the form of a Leopard
+I am the form of a Serpent
+I am the form of a Leopard
+I am the form of a Lion
+```
+
+## New Coding Concepts
+
+### Changing An Objects Class At Runtime.
+
+You change the class of an object by running `self.__class__ = SomeOtherClass`
+
+Note that doing this does not affect any variables created during initialisation, eg `self.variable_name = 'abc'`, since the object itself hasn't changed. Only its class methods and static attributes have been replaced with the class methods and static attributes of the other class.
+
+This explains how calling `tell_me_the_future()` and `tell_me_your_form()` produced different results after changing `self.__class__`
+
+### Avoiding Circular Imports.
+
+Normally in all the examples so far, I have been importing using the form
+
+``` python
+from module import Class
+```
+
+In [/proxy/client.py](/proxy/client.py) I import the `Lion` module. The `Lion` module itself imports the `Leopard` and `Serpent` modules, which in turn also re import the `Lion` module again. This is a circular import and occurs in some situations when you separate your modules into individual files.
+
+Circular imports will prevent the python interpreter from compiling your `.py` file into byte code.
+
+The error will appear like,
+
+```
+cannot import name 'Lion' from partially initialized module 'lion' (most likely due to a circular import)
+```
+
+To avoid circular import errors, you can import modules using the form.
+
+``` python
+import module
+```
+
+and when the import is actually needed in some method
+
+``` python
+OBJECT = module.ClassName
+```
+
+See the [Lion](/proxy/lion.py), [Serpent](/proxy/serpent.py) and [Leopard](/proxy/leopard.py) classes for examples.
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/proxy/classes.dot b/proxy/classes.dot
deleted file mode 100644
index a03a778..0000000
--- a/proxy/classes.dot
+++ /dev/null
@@ -1,10 +0,0 @@
-digraph "classes" {
-charset="utf-8"
-rankdir=BT
-"0" [label="{IComponent|\l|method()\l}", shape="record"];
-"1" [label="{Component|\l|method()\l}", shape="record"];
-"2" [label="{ProxyComponent|component\l|method()\l}", shape="record"];
-"1" -> "0" [arrowhead="empty", arrowtail="none"];
-"2" -> "0" [arrowhead="empty", arrowtail="none"];
-"1" -> "2" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="component", style="solid"];
-}
diff --git a/proxy/client.py b/proxy/client.py
new file mode 100644
index 0000000..639fbe0
--- /dev/null
+++ b/proxy/client.py
@@ -0,0 +1,15 @@
+"The Proxy Example Use Case"
+
+from lion import Lion
+
+PROTEUS = Lion()
+PROTEUS.tell_me_your_form()
+PROTEUS.tell_me_the_future()
+PROTEUS.tell_me_your_form()
+PROTEUS.tell_me_the_future()
+PROTEUS.tell_me_your_form()
+PROTEUS.tell_me_the_future()
+PROTEUS.tell_me_your_form()
+PROTEUS.tell_me_the_future()
+PROTEUS.tell_me_your_form()
+PROTEUS.tell_me_the_future()
diff --git a/proxy/interface_proteus.py b/proxy/interface_proteus.py
new file mode 100644
index 0000000..44db618
--- /dev/null
+++ b/proxy/interface_proteus.py
@@ -0,0 +1,17 @@
+"The Proteus Interface"
+
+from abc import ABCMeta, abstractmethod
+
+
+class IProteus(metaclass=ABCMeta): # pylint: disable=too-few-public-methods
+ "A Greek mythological character that can change to many forms"
+
+ @staticmethod
+ @abstractmethod
+ def tell_me_the_future():
+ "Proteus will change form rather than tell you the future"
+
+ @staticmethod
+ @abstractmethod
+ def tell_me_your_form():
+ "The form of Proteus is elusive like the sea"
diff --git a/proxy/leopard.py b/proxy/leopard.py
new file mode 100644
index 0000000..83e22bf
--- /dev/null
+++ b/proxy/leopard.py
@@ -0,0 +1,19 @@
+"A Leopard Class"
+import random
+from interface_proteus import IProteus
+import lion
+import serpent
+
+
+class Leopard(IProteus): # pylint: disable=too-few-public-methods
+ "Proteus in the form of a Leopard"
+
+ name = "Leopard"
+
+ def tell_me_the_future(self):
+ "Proteus will change to something random"
+ self.__class__ = serpent.Serpent if random.randint(0, 1) else lion.Lion
+
+ @classmethod
+ def tell_me_your_form(cls):
+ print("I am the form of a " + cls.name)
diff --git a/proxy/lion.py b/proxy/lion.py
new file mode 100644
index 0000000..87ed900
--- /dev/null
+++ b/proxy/lion.py
@@ -0,0 +1,20 @@
+"A Lion Class"
+import random
+from interface_proteus import IProteus
+import leopard
+import serpent
+
+
+class Lion(IProteus): # pylint: disable=too-few-public-methods
+ "Proteus in the form of a Lion"
+
+ name = "Lion"
+
+ def tell_me_the_future(self):
+ "Proteus will change to something random"
+ self.__class__ = leopard.Leopard if random.randint(
+ 0, 1) else serpent.Serpent
+
+ @classmethod
+ def tell_me_your_form(cls):
+ print("I am the form of a " + cls.name)
diff --git a/proxy/log.txt b/proxy/log.txt
deleted file mode 100644
index 40cc8e3..0000000
--- a/proxy/log.txt
+++ /dev/null
@@ -1 +0,0 @@
-2019-10-10 14:33:31.314205 : method was proxied
diff --git a/proxy/proxy.png b/proxy/proxy.png
deleted file mode 100644
index fe8de44..0000000
Binary files a/proxy/proxy.png and /dev/null differ
diff --git a/proxy/proxy.py b/proxy/proxy.py
deleted file mode 100644
index a4052af..0000000
--- a/proxy/proxy.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from abc import ABCMeta, abstractmethod
-import datetime
-
-
-class IComponent(metaclass=ABCMeta):
- @staticmethod
- @abstractmethod
- def method(self):
- """A method to implement"""
-
-
-class Component(IComponent):
- def method(self):
- print("The method has been called")
-
-
-class ProxyComponent(IComponent):
- def __init__(self):
- self.component = Component()
-
- def method(self):
- f = open("log.txt", "a")
- f.write("%s : method was proxied\n" % (datetime.datetime.now()))
- self.component.method()
-
-
-COMPONENT1 = Component()
-COMPONENT1.method()
-
-COMPONENT2 = ProxyComponent()
-COMPONENT2.method()
diff --git a/proxy/proxy_concept.py b/proxy/proxy_concept.py
new file mode 100644
index 0000000..bdbe2f9
--- /dev/null
+++ b/proxy/proxy_concept.py
@@ -0,0 +1,58 @@
+# pylint: disable=too-few-public-methods
+# pylint: disable=arguments-differ
+"A Proxy Concept Example"
+
+from abc import ABCMeta, abstractmethod
+
+
+class ISubject(metaclass=ABCMeta):
+ "An interface implemented by both the Proxy and Real Subject"
+ @staticmethod
+ @abstractmethod
+ def request():
+ "A method to implement"
+
+
+class RealSubject(ISubject):
+ "The actual real object that the proxy is representing"
+
+ def __init__(self):
+ # hypothetically enormous amounts of data
+ self.enormous_data = [1, 2, 3]
+
+ def request(self):
+ return self.enormous_data
+
+
+class Proxy(ISubject):
+ """
+ The proxy. In this case the proxy will act as a cache for
+ `enormous_data` and only populate the enormous_data when it
+ is actually necessary
+ """
+
+ def __init__(self):
+ self.enormous_data = []
+ self.real_subject = RealSubject()
+
+ def request(self):
+ """
+ Using the proxy as a cache, and loading data into it only if
+ it is needed
+ """
+ if not self.enormous_data:
+ print("pulling data from RealSubject")
+ self.enormous_data = self.real_subject.request()
+ return self.enormous_data
+ print("pulling data from Proxy cache")
+ return self.enormous_data
+
+
+# The Client
+SUBJECT = Proxy()
+# use SUBJECT
+print(id(SUBJECT))
+# load the enormous amounts of data because now we want to show it.
+print(SUBJECT.request())
+# show the data again, but this time it retrieves it from the local cache
+print(SUBJECT.request())
diff --git a/proxy/serpent.py b/proxy/serpent.py
new file mode 100644
index 0000000..8222731
--- /dev/null
+++ b/proxy/serpent.py
@@ -0,0 +1,19 @@
+"A Serpent Class"
+import random
+from interface_proteus import IProteus
+import lion
+import leopard
+
+
+class Serpent(IProteus): # pylint: disable=too-few-public-methods
+ "Proteus in the form of a Serpent"
+
+ name = "Serpent"
+
+ def tell_me_the_future(self):
+ "Proteus will change to something random"
+ self.__class__ = leopard.Leopard if random.randint(0, 1) else lion.Lion
+
+ @classmethod
+ def tell_me_your_form(cls):
+ print("I am the form of a " + cls.name)
diff --git a/singleton/README.md b/singleton/README.md
new file mode 100644
index 0000000..c2cebd3
--- /dev/null
+++ b/singleton/README.md
@@ -0,0 +1,136 @@
+# Singleton Design Pattern
+
+## Videos
+
+Section | Video Links
+-|-
+Singleton Overview |
+Singleton Use Case |
+Python **Dictionary** |
+
+## Book
+
+Cover | Links |
+-|-|
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC|
+
+## Overview
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Singleton UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./singleton/singleton_concept.py
+id(Singleton) = 2164775087968
+id(OBJECT1) = 2164775087968
+id(OBJECT2) = 2164775087968
+id(OBJECT3) = 2164775087968
+```
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./singleton/client.py
+-----------Leaderboard-----------
+| 1 | Emmy |
+| 2 | Cosmo |
+| 3 | Sean |
+
+-----------Leaderboard-----------
+| 1 | Emmy |
+| 2 | Cosmo |
+| 3 | Sean |
+
+-----------Leaderboard-----------
+| 1 | Emmy |
+| 2 | Cosmo |
+| 3 | Sean |
+```
+
+## New Coding Concepts
+
+### Python Dictionary
+
+In the file [/singleton/leaderboard.py](/singleton/leaderboard.py),
+
+``` python linenums="4"
+
+ "The Leaderboard as a Singleton"
+ _table = {}
+
+```
+
+The `{}` is indicating a Python **Dictionary**.
+
+A Dictionary can be instantiated using the curly braces `{}` or `dict()`
+
+The Dictionary is similar to a [List](/builder#python-list), except that the items are `key:value` pairs.
+
+The Dictionary can store multiple `key:value` pairs, they can be changed, can be added and removed, can be re-ordered, can be pre-filled with `key:value` pairs when instantiated and is very flexible.
+
+Since Python 3.7, dictionaries are ordered in the same way that they are created.
+
+The keys of the dictionary are unique.
+
+You can refer to the dictionary items by key, which will return the value.
+
+``` python
+PS> python
+>>> items = {"abc": 123, "def": 456, "ghi": 789}
+>>> items["abc"]
+123
+```
+
+You can change the value at a key,
+
+``` python
+PS> python
+>>> items = {"abc": 123, "def": 456, "ghi": 789}
+>>> items["def"] = 101112
+>>> items["def"]
+101112
+```
+
+You can add new `key:value` pairs, and remove them by using the key.
+
+``` python
+PS> python
+>>> items = {"abc": 123, "def": 456, "ghi": 789}
+>>> items["jkl"] = 101112
+>>> items["jkl"]
+101112
+>>> items.pop('def')
+456
+>>> items
+{'abc': 123, 'ghi': 789, 'jkl': 101112}
+```
+
+You can order a dictionary alphabetically by key
+
+``` python
+PS> python
+>>> items = {"abc": 123, "ghi": 789, "def": 456}
+>>> items
+{'abc': 123, 'ghi': 789, 'def': 456}
+>>> dict(sorted(items.items()))
+{'abc': 123, 'def': 456, 'ghi': 789}
+```
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/singleton/client.py b/singleton/client.py
new file mode 100644
index 0000000..2aabc8e
--- /dev/null
+++ b/singleton/client.py
@@ -0,0 +1,23 @@
+# pylint: disable=too-few-public-methods
+
+"Singleton Use Case Example Code."
+
+from game1 import Game1
+from game2 import Game2
+from game3 import Game3
+
+
+# The Client
+# All games share and manage the same leaderboard because it is a singleton.
+GAME1 = Game1()
+GAME1.add_winner(2, "Cosmo")
+
+GAME2 = Game2()
+GAME2.add_winner(3, "Sean")
+
+GAME3 = Game3()
+GAME3.add_winner(1, "Emmy")
+
+GAME1.leaderboard.print()
+GAME2.leaderboard.print()
+GAME3.leaderboard.print()
diff --git a/singleton/game1.py b/singleton/game1.py
new file mode 100644
index 0000000..e20b3e8
--- /dev/null
+++ b/singleton/game1.py
@@ -0,0 +1,15 @@
+# pylint: disable=too-few-public-methods
+"A Game Class that uses the Leaderboard Singleton"
+
+from leaderboard import Leaderboard
+from interface_game import IGame
+
+
+class Game1(IGame):
+ "Game1 implements IGame"
+
+ def __init__(self):
+ self.leaderboard = Leaderboard()
+
+ def add_winner(self, position, name):
+ self.leaderboard.add_winner(position, name)
diff --git a/singleton/game2.py b/singleton/game2.py
new file mode 100644
index 0000000..943f59c
--- /dev/null
+++ b/singleton/game2.py
@@ -0,0 +1,14 @@
+"A Game Class that uses the Leaderboard Singleton"
+
+from leaderboard import Leaderboard
+from interface_game import IGame
+
+
+class Game2(IGame): # pylint: disable=too-few-public-methods
+ "Game2 implements IGame"
+
+ def __init__(self):
+ self.leaderboard = Leaderboard()
+
+ def add_winner(self, position, name):
+ self.leaderboard.add_winner(position, name)
diff --git a/singleton/game3.py b/singleton/game3.py
new file mode 100644
index 0000000..3c10608
--- /dev/null
+++ b/singleton/game3.py
@@ -0,0 +1,6 @@
+"A Game Class that uses the Leaderboard Singleton"
+from game2 import Game2
+
+
+class Game3(Game2): # pylint: disable=too-few-public-methods
+ """Game 3 Inherits from Game 2 instead of implementing IGame"""
diff --git a/singleton/interface_game.py b/singleton/interface_game.py
new file mode 100644
index 0000000..2f2cff0
--- /dev/null
+++ b/singleton/interface_game.py
@@ -0,0 +1,13 @@
+# pylint: disable=too-few-public-methods
+
+"A Game Interface"
+
+from abc import ABCMeta, abstractmethod
+
+
+class IGame(metaclass=ABCMeta):
+ "A Game Interface"
+ @staticmethod
+ @abstractmethod
+ def add_winner(position, name):
+ "Must implement add_winner"
diff --git a/singleton/leaderboard.py b/singleton/leaderboard.py
new file mode 100644
index 0000000..f116ca4
--- /dev/null
+++ b/singleton/leaderboard.py
@@ -0,0 +1,22 @@
+"A Leaderboard Singleton Class"
+
+
+class Leaderboard():
+ "The Leaderboard as a Singleton"
+ _table = {}
+
+ def __new__(cls):
+ return cls
+
+ @classmethod
+ def print(cls):
+ "A class level method"
+ print("-----------Leaderboard-----------")
+ for key, value in sorted(cls._table.items()):
+ print(f"|\t{key}\t|\t{value}\t|")
+ print()
+
+ @classmethod
+ def add_winner(cls, position, name):
+ "A class level method"
+ cls._table[position] = name
diff --git a/singleton/singleton_concept.py b/singleton/singleton_concept.py
new file mode 100644
index 0000000..1d9d869
--- /dev/null
+++ b/singleton/singleton_concept.py
@@ -0,0 +1,37 @@
+# pylint: disable=too-few-public-methods
+"Singleton Concept Sample Code"
+import copy
+
+
+class Singleton():
+ "The Singleton Class"
+ value = []
+
+ def __new__(cls):
+ return cls
+
+ # def __init__(self):
+ # print("in init")
+
+ @staticmethod
+ def static_method():
+ "Use @staticmethod if no inner variables required"
+
+ @classmethod
+ def class_method(cls):
+ "Use @classmethod to access class level variables"
+ print(cls.value)
+
+
+# The Client
+# All uses of singleton point to the same memory address (id)
+print(f"id(Singleton)\t= {id(Singleton)}")
+
+OBJECT1 = Singleton()
+print(f"id(OBJECT1)\t= {id(OBJECT1)}")
+
+OBJECT2 = copy.deepcopy(OBJECT1)
+print(f"id(OBJECT2)\t= {id(OBJECT2)}")
+
+OBJECT3 = Singleton()
+print(f"id(OBJECT1)\t= {id(OBJECT3)}")
diff --git a/state/README.md b/state/README.md
new file mode 100644
index 0000000..4f1fce5
--- /dev/null
+++ b/state/README.md
@@ -0,0 +1,95 @@
+# State Design Pattern
+
+## Videos
+
+Section | Video Links
+-|-
+State Overview |
+State Use Case |
+**\_\_call\_\_** Attribute |
+
+## Book
+
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
+
+## Overview
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## State UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+### Output
+
+``` bash
+python.exe ./state/state_concept.py
+I am ConcreteStateB
+I am ConcreteStateA
+I am ConcreteStateB
+I am ConcreteStateA
+I am ConcreteStateC
+```
+
+## State Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## State Example Use Case UML Diagram
+
+
+
+## Output
+
+``` bash
+python.exe ./state/client.py
+Task Started
+Task Running
+Task Finished
+Task Started
+Task Running
+```
+
+## New Coding Concepts
+
+### Dunder `__call__` Method
+
+Overloading the `__call__` method makes an instance of a class callable like a function when by default it isn't. You need to call a method within the class directly.
+
+``` python
+class ExampleClass:
+ @staticmethod
+ def do_this_by_default():
+ print("doing this")
+
+EXAMPLE = ExampleClass()
+EXAMPLE.do_this_by_default() # needs to be explicitly called to execute
+```
+
+If you want a default method in your class, you can point to it using by the `__call__` method.
+
+``` python
+class ExampleClass:
+ @staticmethod
+ def do_this_by_default():
+ print("doing this")
+
+ __call__ = do_this_by_default
+
+EXAMPLE = ExampleClass()
+EXAMPLE() # function now gets called by default
+```
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/state/client.py b/state/client.py
new file mode 100644
index 0000000..19e1e57
--- /dev/null
+++ b/state/client.py
@@ -0,0 +1,76 @@
+# pylint: disable=too-few-public-methods
+"The State Use Case Example"
+from abc import ABCMeta, abstractmethod
+
+
+class Context():
+ "This is the object whose behavior will change"
+
+ def __init__(self):
+
+ self.state_handles = [
+ Started(),
+ Running(),
+ Finished()
+ ]
+ self._handle = iter(self.state_handles)
+
+ def request(self):
+ "Each time the request is called, a new class will handle it"
+ try:
+ self._handle.__next__()()
+ except StopIteration:
+ # resetting so it loops
+ self._handle = iter(self.state_handles)
+
+
+class IState(metaclass=ABCMeta):
+ "A State Interface"
+
+ @staticmethod
+ @abstractmethod
+ def __call__():
+ "Set the default method"
+
+
+class Started(IState):
+ "A ConcreteState Subclass"
+
+ @staticmethod
+ def method():
+ "A task of this class"
+ print("Task Started")
+
+ __call__ = method
+
+
+class Running(IState):
+ "A ConcreteState Subclass"
+
+ @staticmethod
+ def method():
+ "A task of this class"
+ print("Task Running")
+
+ __call__ = method
+
+
+class Finished(IState):
+ "A ConcreteState Subclass"
+
+ @staticmethod
+ def method():
+ "A task of this class"
+ print("Task Finished")
+
+ __call__ = method
+
+
+# The Client
+CONTEXT = Context()
+CONTEXT.request()
+CONTEXT.request()
+CONTEXT.request()
+CONTEXT.request()
+CONTEXT.request()
+CONTEXT.request()
diff --git a/state/state_concept.py b/state/state_concept.py
new file mode 100644
index 0000000..20b4cf3
--- /dev/null
+++ b/state/state_concept.py
@@ -0,0 +1,53 @@
+# pylint: disable=too-few-public-methods
+"The State Pattern Concept"
+from abc import ABCMeta, abstractmethod
+import random
+
+class Context():
+ "This is the object whose behavior will change"
+
+ def __init__(self):
+ self.state_handles = [ConcreteStateA(),
+ ConcreteStateB(),
+ ConcreteStateC()]
+ self.handle = None
+
+ def request(self):
+ """A method of the state that dynamically changes which
+ class it uses depending on the value of self.handle"""
+ self.handle = self.state_handles[random.randint(0, 2)]
+ return self.handle
+
+class IState(metaclass=ABCMeta):
+ "A State Interface"
+
+ @staticmethod
+ @abstractmethod
+ def __str__():
+ "Set the default method"
+
+class ConcreteStateA(IState):
+ "A ConcreteState Subclass"
+
+ def __str__(self):
+ return "I am ConcreteStateA"
+
+class ConcreteStateB(IState):
+ "A ConcreteState Subclass"
+
+ def __str__(self):
+ return "I am ConcreteStateB"
+
+class ConcreteStateC(IState):
+ "A ConcreteState Subclass"
+
+ def __str__(self):
+ return "I am ConcreteStateC"
+
+# The Client
+CONTEXT = Context()
+print(CONTEXT.request())
+print(CONTEXT.request())
+print(CONTEXT.request())
+print(CONTEXT.request())
+print(CONTEXT.request())
diff --git a/strategy/README.md b/strategy/README.md
new file mode 100644
index 0000000..8f1a389
--- /dev/null
+++ b/strategy/README.md
@@ -0,0 +1,60 @@
+# Strategy Design Pattern
+
+## Videos
+
+Section | Video Links
+-|-
+Strategy Overview |
+Strategy Use Case |
+
+## Book
+
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
+
+## Overview
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Strategy UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Output
+
+``` bash
+python ./strategy/strategy_concept.py
+I am ConcreteStrategyA
+I am ConcreteStrategyB
+I am ConcreteStrategyC
+```
+
+## Strategy Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Strategy Example Use Case UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./strategy/client.py
+I am Walking. New position = [1, 0]
+I am Running. New position = [3, 0]
+I am Crawling. New position = [3.5, 0]
+```
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/strategy/client.py b/strategy/client.py
new file mode 100644
index 0000000..33eca01
--- /dev/null
+++ b/strategy/client.py
@@ -0,0 +1,68 @@
+# pylint: disable=too-few-public-methods
+"The Strategy Pattern Example Use Case"
+from abc import ABCMeta, abstractmethod
+
+
+class GameCharacter():
+ "This is the context whose strategy will change"
+
+ position = [0, 0]
+
+ @classmethod
+ def move(cls, movement_style):
+ "The movement algorithm has been decided by the client"
+ movement_style(cls.position)
+
+
+class IMove(metaclass=ABCMeta):
+ "A Concrete Strategy Interface"
+
+ @staticmethod
+ @abstractmethod
+ def __call__():
+ "Implementors must select the default method"
+
+
+class Walking(IMove):
+ "A Concrete Strategy Subclass"
+
+ @staticmethod
+ def walk(position):
+ "A walk algorithm"
+ position[0] += 1
+ print(f"I am Walking. New position = {position}")
+
+ __call__ = walk
+
+
+class Running(IMove):
+ "A Concrete Strategy Subclass"
+
+ @staticmethod
+ def run(position):
+ "A run algorithm"
+ position[0] += 2
+ print(f"I am Running. New position = {position}")
+
+ __call__ = run
+
+
+class Crawling(IMove):
+ "A Concrete Strategy Subclass"
+
+ @staticmethod
+ def crawl(position):
+ "A crawl algorithm"
+ position[0] += 0.5
+ print(f"I am Crawling. New position = {position}")
+
+ __call__ = crawl
+
+
+# The Client
+GAME_CHARACTER = GameCharacter()
+GAME_CHARACTER.move(Walking())
+# Character sees the enemy
+GAME_CHARACTER.move(Running())
+# Character finds a small cave to hide in
+GAME_CHARACTER.move(Crawling())
diff --git a/strategy/strategy_concept.py b/strategy/strategy_concept.py
new file mode 100644
index 0000000..234118a
--- /dev/null
+++ b/strategy/strategy_concept.py
@@ -0,0 +1,50 @@
+# pylint: disable=too-few-public-methods
+"The Strategy Pattern Concept"
+from abc import ABCMeta, abstractmethod
+
+
+class Context():
+ "This is the object whose behavior will change"
+
+ @staticmethod
+ def request(strategy):
+ """The request is handled by the class passed in"""
+ return strategy()
+
+
+class IStrategy(metaclass=ABCMeta):
+ "A strategy Interface"
+
+ @staticmethod
+ @abstractmethod
+ def __str__():
+ "Implement the __str__ dunder"
+
+
+class ConcreteStrategyA(IStrategy):
+ "A Concrete Strategy Subclass"
+
+ def __str__(self):
+ return "I am ConcreteStrategyA"
+
+
+class ConcreteStrategyB(IStrategy):
+ "A Concrete Strategy Subclass"
+
+ def __str__(self):
+ return "I am ConcreteStrategyB"
+
+
+class ConcreteStrategyC(IStrategy):
+ "A Concrete Strategy Subclass"
+
+ def __str__(self):
+ return "I am ConcreteStrategyC"
+
+
+# The Client
+CONTEXT = Context()
+
+print(CONTEXT.request(ConcreteStrategyA))
+print(CONTEXT.request(ConcreteStrategyB))
+print(CONTEXT.request(ConcreteStrategyC))
diff --git a/template/README.md b/template/README.md
new file mode 100644
index 0000000..fe00545
--- /dev/null
+++ b/template/README.md
@@ -0,0 +1,79 @@
+# Template Method Design Pattern
+
+## Videos
+
+Section | Video Links
+-|-
+Template Method Overview |
+Template Method Use Case |
+
+## Book
+
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
+
+## Overview
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Template Method UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Output
+
+``` bash
+python ./template/template_concept.py
+Class_A : Step Two (overridden)
+Step Three is a hook that prints this line by default.
+Class_B : Step One (overridden)
+Class_B : Step Two. (overridden)
+Class_B : Step Three. (overridden)
+```
+
+## Template Method Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Template Method Use Case UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./template/client.py
+----------------------
+title : New Text Document
+background_colour : white
+text : Some Text
+footer : -- Page 1 --
+
+
+
+ Line 1
+Line 2
+ + +``` + +## Summary + +_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._ \ No newline at end of file diff --git a/template/abstract_document.py b/template/abstract_document.py new file mode 100644 index 0000000..c4b770d --- /dev/null +++ b/template/abstract_document.py @@ -0,0 +1,53 @@ +"An abstract document containing a combination of hooks and abstract methods" +from abc import ABCMeta, abstractmethod + + +class AbstractDocument(metaclass=ABCMeta): + "A template class containing a template method and primitive methods" + + @staticmethod + @abstractmethod + def title(document): + "must implement" + + @staticmethod + def description(document): + "optional" + + @staticmethod + def author(document): + "optional" + + @staticmethod + def background_colour(document): + "optional with a default behavior" + document["background_colour"] = "white" + + @staticmethod + @abstractmethod + def text(document, text): + "must implement" + + @staticmethod + def footer(document): + "optional" + + @staticmethod + def print(document): + "optional with a default behavior" + print("----------------------") + for attribute in document: + print(f"{attribute}\t: {document[attribute]}") + print() + + @classmethod + def create_document(cls, text): + "The template method" + _document = {} + cls.title(_document) + cls.description(_document) + cls.author(_document) + cls.background_colour(_document) + cls.text(_document, text) + cls.footer(_document) + cls.print(_document) diff --git a/template/client.py b/template/client.py new file mode 100644 index 0000000..e6e108f --- /dev/null +++ b/template/client.py @@ -0,0 +1,9 @@ +"The Template Pattern Use Case Example" +from text_document import TextDocument +from html_document import HTMLDocument + +TEXT_DOCUMENT = TextDocument() +TEXT_DOCUMENT.create_document("Some Text") + +HTML_DOCUMENT = HTMLDocument() +HTML_DOCUMENT.create_document("Line 1\nLine 2") diff --git a/template/html_document.py b/template/html_document.py new file mode 100644 index 0000000..fb1a41c --- /dev/null +++ b/template/html_document.py @@ -0,0 +1,43 @@ +"A HTML document concrete class of AbstractDocument" +from abstract_document import AbstractDocument + + +class HTMLDocument(AbstractDocument): + "Prints out a HTML formatted document" + @staticmethod + def title(document): + document["title"] = "New HTML Document" + + @staticmethod + def text(document, text): + "Putting multiple lines into there own p tags" + lines = text.splitlines() + markup = "" + for line in lines: + markup = markup + "" + f"{line}
\n" + document["text"] = markup[:-1] + + @staticmethod + def print(document): + "overriding print to output with html tags" + print("") + print(" ") + for attribute in document: + if attribute in ["title", "description", "author"]: + print( + f" <{attribute}>{document[attribute]}" + f"{attribute}>" + ) + if attribute == "background_colour": + print(" ") + print(" ") + print(" ") + print(f"{document['text']}") + print(" ") + print("") diff --git a/template/template_concept.py b/template/template_concept.py new file mode 100644 index 0000000..635d8bf --- /dev/null +++ b/template/template_concept.py @@ -0,0 +1,74 @@ +# pylint: disable=too-few-public-methods +"The Template Method Pattern Concept" +from abc import ABCMeta, abstractmethod + + +class AbstractClass(metaclass=ABCMeta): + "A template class containing a template method and primitive methods" + + @staticmethod + def step_one(): + """ + Hooks are normally empty in the abstract class. The + implementing class can optionally override providing a custom + implementation + """ + + @staticmethod + @abstractmethod + def step_two(): + """ + An abstract method that must be overridden in the implementing + class. It has been given `@abstractmethod` decorator so that + pylint shows the error. + """ + + @staticmethod + def step_three(): + """ + Hooks can also contain default behavior and can be optionally + overridden + """ + print("Step Three is a hook that prints this line by default.") + + @classmethod + def template_method(cls): + """ + This is the template method that the subclass will call. + The subclass (implementing class) doesn't need to override this + method since it has would have already optionally overridden + the following methods with its own implementations + """ + cls.step_one() + cls.step_two() + cls.step_three() + + +class ConcreteClassA(AbstractClass): + "A concrete class that only overrides step two" + @staticmethod + def step_two(): + print("Class_A : Step Two (overridden)") + + +class ConcreteClassB(AbstractClass): + "A concrete class that only overrides steps one, two and three" + @staticmethod + def step_one(): + print("Class_B : Step One (overridden)") + + @staticmethod + def step_two(): + print("Class_B : Step Two. (overridden)") + + @staticmethod + def step_three(): + print("Class_B : Step Three. (overridden)") + + +# The Client +CLASS_A = ConcreteClassA() +CLASS_A.template_method() + +CLASS_B = ConcreteClassB() +CLASS_B.template_method() diff --git a/template/text_document.py b/template/text_document.py new file mode 100644 index 0000000..7f2784b --- /dev/null +++ b/template/text_document.py @@ -0,0 +1,17 @@ +"A text document concrete class of AbstractDocument" +from abstract_document import AbstractDocument + + +class TextDocument(AbstractDocument): + "Prints out a text document" + @staticmethod + def title(document): + document["title"] = "New Text Document" + + @staticmethod + def text(document, text): + document["text"] = text + + @staticmethod + def footer(document): + document["footer"] = "-- Page 1 --" diff --git a/visitor/README.md b/visitor/README.md new file mode 100644 index 0000000..1f969de --- /dev/null +++ b/visitor/README.md @@ -0,0 +1,144 @@ +# Visitor Design Pattern + +## Videos + +Section | Video Links +-|- +Visitor Overview |
+Visitor Use Case |
+hasattr() Method |
+expandtabs() Method |
+
+## Book
+
+Cover | Links
+-|-
+ |
https://www.amazon.com/dp/B08XLJ8Z2J
https://www.amazon.co.uk/dp/B08XLJ8Z2J
https://www.amazon.in/dp/B08Z282SBC
https://www.amazon.de/dp/B08XLJ8Z2J
https://www.amazon.fr/dp/B08XLJ8Z2J
https://www.amazon.es/dp/B08XLJ8Z2J
https://www.amazon.it/dp/B08XLJ8Z2J
https://www.amazon.co.jp/dp/B08XLJ8Z2J
https://www.amazon.ca/dp/B08XLJ8Z2J
https://www.amazon.com.au/dp/B08Z282SBC
+
+## Overview
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Terminology
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Visitor UML Diagram
+
+
+
+## Source Code
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Output
+
+``` bash
+python ./visitor/visitor_concept.py
+D
+B
+C
+A
+561
+```
+
+## Visitor Example Use Case
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
+
+## Visitor Example UML Diagram
+
+
+
+## Output
+
+``` bash
+python ./visitor/client.py
+Utility :ABC-123-21
+V8 engine :DEF-456-21
+FrontLeft :GHI-789FL-21
+FrontRight :GHI-789FR-21
+BackLeft :GHI-789BL-21
+BackRight :GHI-789BR-21
+Total Price = 4132
+```
+
+## New Coding Concepts
+
+### Instance `hasattr()`
+
+In the Visitor objects in the example use case above, I test if the elements have a certain attribute during the visit operation.
+
+``` python
+def visit(cls, element):
+ if hasattr(element, 'price'):
+ ...
+```
+
+The `hasattr()` method can be used to test if an instantiated object has an attribute of a particular name.
+
+``` python
+class ClassA():
+ name = "abc"
+ value = 123
+
+CLASS_A = ClassA()
+print(hasattr(CLASS_A, "name"))
+print(hasattr(CLASS_A, "value"))
+print(hasattr(CLASS_A, "date"))
+
+```
+
+Outputs
+
+``` bash
+True
+True
+False
+```
+
+### String `expandtabs()`
+
+When printing strings to the console, you can include special characters `\t` that print a series of extra spaces called tabs. The tabs help present multiline text in a more tabular form which appears to be neater to look at.
+
+``` bash
+abc 123
+defg 456
+hi 78910
+```
+
+The number of spaces added depends on the size of the word before the `\t` character in the string. By default, a tab makes up 8 spaces.
+
+Now, not all words separated by a tab will line up the same on the next line.
+
+``` bash
+abcdef 123
+cdefghij 4563
+ghi 789
+jklmn 1011
+
+```
+
+The problem occurs usually when a word is already 8 or more characters long.
+
+To help solve the spacing issue, you can use the `expandtabs()` method on a string to set how many characters a tab will use.
+
+``` python
+print("abcdef\t123".expandtabs(10))
+print("cdefghij\t4563".expandtabs(10))
+print("ghi\t789".expandtabs(10))
+print("jklmn\t1011".expandtabs(10))
+```
+
+Now outputs
+
+``` bash
+abcdef 123
+cdefghij 4563
+ghi 789
+jklmn 1011
+```
+
+## Summary
+
+_... Refer to [Book](https://amzn.to/466lBN6) or [Design Patterns In Python website](https://sbcode.net/python/) to read textual content._
\ No newline at end of file
diff --git a/visitor/client.py b/visitor/client.py
new file mode 100644
index 0000000..293cd4a
--- /dev/null
+++ b/visitor/client.py
@@ -0,0 +1,143 @@
+# pylint: disable=too-few-public-methods
+# pylint: disable=arguments-differ
+"The Visitor Pattern Use Case Example"
+from abc import ABCMeta, abstractmethod
+
+
+class IVisitor(metaclass=ABCMeta):
+ "An interface that custom Visitors should implement"
+ @staticmethod
+ @abstractmethod
+ def visit(element):
+ "Visitors visit Elements/Objects within the application"
+
+
+class IVisitable(metaclass=ABCMeta):
+ """
+ An interface that concrete objects should implement that allows
+ the visitor to traverse a hierarchical structure of objects
+ """
+ @staticmethod
+ @abstractmethod
+ def accept(visitor):
+ """
+ The Visitor traverses and accesses each object through this
+ method
+ """
+
+
+class AbstractCarPart():
+ "The Abstract Car Part"
+ @property
+ def name(self):
+ "a name for the part"
+ return self._name
+
+ @name.setter
+ def name(self, value):
+ self._name = value
+
+ @property
+ def sku(self):
+ "The Stock Keeping Unit (sku)"
+ return self._sku
+
+ @sku.setter
+ def sku(self, value):
+ self._sku = value
+
+ @property
+ def price(self):
+ "The price per unit"
+ return self._price
+
+ @price.setter
+ def price(self, value):
+ self._price = value
+
+
+class Body(AbstractCarPart, IVisitable):
+ "A part of the car"
+
+ def __init__(self, name, sku, price):
+ self.name = name
+ self.sku = sku
+ self.price = price
+
+ def accept(self, visitor):
+ visitor.visit(self)
+
+
+class Engine(AbstractCarPart, IVisitable):
+ "A part of the car"
+
+ def __init__(self, name, sku, price):
+ self.name = name
+ self.sku = sku
+ self.price = price
+
+ def accept(self, visitor):
+ visitor.visit(self)
+
+
+class Wheel(AbstractCarPart, IVisitable):
+ "A part of the car"
+
+ def __init__(self, name, sku, price):
+ self.name = name
+ self.sku = sku
+ self.price = price
+
+ def accept(self, visitor):
+ visitor.visit(self)
+
+
+class Car(AbstractCarPart, IVisitable):
+ "A Car with parts"
+
+ def __init__(self, name):
+ self.name = name
+ self._parts = [
+ Body("Utility", "ABC-123-21", 1001),
+ Engine("V8 engine", "DEF-456-21", 2555),
+ Wheel("FrontLeft", "GHI-789FL-21", 136),
+ Wheel("FrontRight", "GHI-789FR-21", 136),
+ Wheel("BackLeft", "GHI-789BL-21", 152),
+ Wheel("BackRight", "GHI-789BR-21", 152),
+ ]
+
+ def accept(self, visitor):
+ for parts in self._parts:
+ parts.accept(visitor)
+ visitor.visit(self)
+
+
+class PrintPartsVisitor(IVisitor):
+ "Print out the part name and sku"
+ @staticmethod
+ def visit(element):
+ if hasattr(element, 'sku'):
+ print(f"{element.name}\t:{element.sku}".expandtabs(6))
+
+
+class TotalPriceVisitor(IVisitor):
+ "Print out the total cost of the parts in the car"
+ total_price = 0
+
+ @classmethod
+ def visit(cls, element):
+ if hasattr(element, 'price'):
+ cls.total_price += element.price
+ return cls.total_price
+
+
+# The Client
+CAR = Car("DeLorean")
+
+# Print out the part name and sku using the PrintPartsVisitor
+CAR.accept(PrintPartsVisitor())
+
+# Calculate the total prince of the parts using the TotalPriceVisitor
+TOTAL_PRICE_VISITOR = TotalPriceVisitor()
+CAR.accept(TOTAL_PRICE_VISITOR)
+print(f"Total Price = {TOTAL_PRICE_VISITOR.total_price}")
diff --git a/visitor/visitor_concept.py b/visitor/visitor_concept.py
new file mode 100644
index 0000000..f3c33fb
--- /dev/null
+++ b/visitor/visitor_concept.py
@@ -0,0 +1,76 @@
+# pylint: disable=too-few-public-methods
+# pylint: disable=arguments-differ
+"The Visitor Pattern Concept"
+from abc import ABCMeta, abstractmethod
+
+class IVisitor(metaclass=ABCMeta):
+ "An interface that custom Visitors should implement"
+ @staticmethod
+ @abstractmethod
+ def visit(element):
+ "Visitors visit Elements/Objects within the application"
+
+class IVisitable(metaclass=ABCMeta):
+ """
+ An interface the concrete objects should implement that allows
+ the visitor to traverse a hierarchical structure of objects
+ """
+ @staticmethod
+ @abstractmethod
+ def accept(visitor):
+ """
+ The Visitor traverses and accesses each object through this
+ method
+ """
+
+class Element(IVisitable):
+ "An Object that can be part of any hierarchy"
+
+ def __init__(self, name, value, parent=None):
+ self.name = name
+ self.value = value
+ self.elements = set()
+ if parent:
+ parent.elements.add(self)
+
+ def accept(self, visitor):
+ "required by the Visitor that will traverse"
+ for element in self.elements:
+ element.accept(visitor)
+ visitor.visit(self)
+
+# The Client
+# Creating an example object hierarchy.
+Element_A = Element("A", 101)
+Element_B = Element("B", 305, Element_A)
+Element_C = Element("C", 185, Element_A)
+Element_D = Element("D", -30, Element_B)
+
+# Now Rather than changing the Element class to support custom
+# operations, we can utilise the accept method that was
+# implemented in the Element class because of the addition of
+# the IVisitable interface
+
+class PrintElementNamesVisitor(IVisitor):
+ "Create a visitor that prints the Element names"
+ @staticmethod
+ def visit(element):
+ print(element.name)
+
+# Using the PrintElementNamesVisitor to traverse the object hierarchy
+Element_A.accept(PrintElementNamesVisitor)
+
+class CalculateElementTotalsVisitor(IVisitor):
+ "Create a visitor that totals the Element values"
+ total_value = 0
+
+ @classmethod
+ def visit(cls, element):
+ cls.total_value += element.value
+ return cls.total_value
+
+# Using the CalculateElementTotalsVisitor to traverse the
+# object hierarchy
+TOTAL = CalculateElementTotalsVisitor()
+Element_A.accept(CalculateElementTotalsVisitor)
+print(TOTAL.total_value)