Skip to content

Commit

Permalink
Add factory pattern
Browse files Browse the repository at this point in the history
Signed-off-by: Darko Draskovic <[email protected]>
  • Loading branch information
darkodraskovic committed Oct 10, 2022
1 parent f20c8fa commit 4183ef4
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 3 deletions.
12 changes: 9 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"python.formatting.provider": "black",
"python.formatting.blackArgs": [
"--line-length",
"96"
],
"python.linting.enabled": true,
"python.linting.mypyEnabled": true,
"python.linting.mypyArgs": [
Expand All @@ -12,10 +16,12 @@
"--strict"
],
"python.linting.pylintEnabled": false,
"python.linting.pylintArgs": ["--disable=C0111"],
"python.linting.pylintArgs": [
"--disable=C0111"
],
"python.linting.flake8Enabled": true,
"python.linting.flake8Args": [
"--max-line-length=88"
"--max-line-length=96"
// "--ignore=E402,F841,F401,E302,E305",
]
}
}
95 changes: 95 additions & 0 deletions src/patterns/factories/abstract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
from abc import ABC
from enum import Enum, auto


# object hierarchy

# abstract object
class HotDrink(ABC):
quantity: int

def consume(self) -> None:
pass


class Tea(HotDrink):
def consume(self) -> None:
print(f"You consume {self.quantity}ml of tea!")


class Coffee(HotDrink):
def consume(self) -> None:
print(f"You consume {self.quantity}ml of coffee!")


# factory hierarchy

# abstract factory; used to mandate interface
# essential in strongly typed languages, optional in Python
class HotDrinkFactory(ABC):
def prepare(self, quantity: int) -> HotDrink:
pass


class TeaFactory(ABC):
def prepare(self, quantity: int) -> HotDrink:
tea = Tea()
tea.quantity = quantity
return tea


class CoffeeFactory(ABC):
def prepare(self, quantity: int) -> HotDrink:
coffee = Coffee()
coffee.quantity = quantity
return coffee


def make_drink(type: str) -> HotDrink | None:
if type == "tea":
return TeaFactory().prepare(200)
elif type == "coffee":
return CoffeeFactory().prepare(50)
else:
return None


class HotDrinkMachine:
factories: list[tuple[str, HotDrinkFactory]] = []
_initialized: bool = False

class AvailableDrink(Enum):
TEA = auto()
COFFEE = auto()

def __init__(self) -> None:
if not self._initialized:
for d in self.AvailableDrink:
name = d.name[0] + d.name[1:].lower()
factory_name = name + "Factory"
factory_instance = eval(factory_name)()
self.factories.append((name, factory_instance))
self._initialized = True
pass

def make_drink(self) -> HotDrink:
print("Available drinks: ")
for i in range(len(self.factories)):
print(str(i) + f": {self.factories[i][0]}")

s = input(f"Pick a drink (0-{len(self.factories)-1}): ")
idx = int(s)
s = input("Specify quantity: ")
quantity = int(s)
return self.factories[idx][1].prepare(quantity)


if __name__ == "__main__":
# entry = input("Please choose a drink: ")
# drink = make_drink(entry)
# if drink:
# drink.consume()

hdm = HotDrinkMachine()
drink = hdm.make_drink()
drink.consume()
93 changes: 93 additions & 0 deletions src/patterns/factories/method.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from math import sin, cos, pi
from enum import Enum


class CoordinateSystem(Enum):
CARTESIAN = 1
POLAR = 2


# class that does not use factory method
class Point_0:
x: float
y: float

def __init__(
self, a: float, b: float, system: CoordinateSystem = CoordinateSystem.CARTESIAN
) -> None:
if system == CoordinateSystem.CARTESIAN:
self.x = a
self.y = b
elif system == CoordinateSystem.POLAR:
self.x = a * cos(b)
self.y = a * sin(b)


# class that uses factory method
class Point:
x: float
y: float

def __init__(self, x: float = 0, y: float = 0) -> None:
self.x = x
self.y = y

def __str__(self) -> str:
return f"x: {self.x}, y: {self.y}"

# factory is a method that creates an object; it is an aleternative to constructors and
# initalizers with multiple, special case parameters
@staticmethod
def new_cartesian(x: float, y: float) -> "Point":
return Point(x, y)

@staticmethod
def new_polar(rho: float, theta: float) -> "Point":
return Point(rho * cos(theta), rho * sin(theta))

# factory can be an inner class (no point doing this in Python, though, becasause
# everything is public)
class PointFactory:
def new_cartesian(self, x: float, y: float) -> "Point":
# use no params constructor to semantically decouple class factory from object class
p: Point = Point()
p.x = x
p.y = y
return p

def new_polar(self, rho: float, theta: float) -> "Point":
return Point(rho * cos(theta), rho * sin(theta))

# singleton factory instance
factory = PointFactory()


# Alternative is to create a factory with factory methods
# factory class is coupled to an object class/definition
class PointFactory:
@staticmethod
def new_cartesian(x: float, y: float) -> "Point":
# use no params constructor to semantically decouple class factory from object class
p: Point = Point()
p.x = x
p.y = y
return p

@staticmethod
def new_polar(rho: float, theta: float) -> "Point":
return Point(rho * cos(theta), rho * sin(theta))


if __name__ == "__main__":
# use constructor
p1 = Point(10, 15)

# use factory methods
p2 = Point.new_cartesian(12, 24.5)

p3 = Point.new_polar(1, pi / 4)

p4 = Point.factory.new_cartesian(1, 1)
print(p4)

print()

0 comments on commit 4183ef4

Please sign in to comment.