Skip to content

Commit 9a36858

Browse files
committed
chapter 9
1 parent ba06a7b commit 9a36858

File tree

13 files changed

+445
-0
lines changed

13 files changed

+445
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
[chapter 7(facade)](https://github.com/rebuild-123/Python-Head-First-Design-Patterns/tree/main/facade/hometheater)
1111
[chapter 8(templatemethod)](https://github.com/rebuild-123/Python-Head-First-Design-Patterns/tree/main/templatemethod) (applet and frame have no translation.)
1212
[chapter 9(iterator)](https://github.com/rebuild-123/Python-Head-First-Design-Patterns/tree/main/iterator)
13+
[chapter 9(composite)](https://github.com/rebuild-123/Python-Head-First-Design-Patterns/tree/main/composite)
1314
## Introduction
1415
1. Translated from [Head-First-Design-Patterns](https://github.com/bethrobson/Head-First-Design-Patterns) (java)
1516
2. What are the differences between other Python version Head-First-Design-Patterns and ours?

composite/menu/Menu.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from typing import Iterator, List
2+
3+
from MenuComponent import MenuComponent
4+
5+
6+
class Menu(MenuComponent):
7+
iterator: Iterator[MenuComponent] = None
8+
menuComponents: List[MenuComponent]
9+
name: str
10+
description: str
11+
12+
def __init__(self, name: str, description: str):
13+
self.name = name
14+
self.description = description
15+
self.menuComponents = []
16+
17+
def add(self, menuComponent: MenuComponent) -> None:
18+
self.menuComponents.append(menuComponent)
19+
20+
def remove(self, menuComponent: MenuComponent) -> None:
21+
self.menuComponents.remove(menuComponent)
22+
23+
def getChild(self, i: int) -> MenuComponent:
24+
return self.menuComponents[i]
25+
26+
def getName(self) -> str:
27+
return self.name
28+
29+
def getDescription(self) -> str:
30+
return self.description
31+
32+
def createIterator(self) -> Iterator[MenuComponent]:
33+
if self.iterator == None:
34+
self.iterator = CompositeIterator(iter(self.menuComponents))
35+
return self.iterator
36+
37+
def print(self) -> None:
38+
print(f'\n{self.getName()}', end="")
39+
print(f", {self.getDescription()}")
40+
print("---------------------")
41+
42+
iterator: Iterator[MenuComponent] = iter(self.menuComponents)
43+
menuComponent: MenuComponent = next(iterator, None)
44+
while menuComponent != None:
45+
menuComponent.print()
46+
menuComponent: MenuComponent = next(iterator, None)

composite/menu/MenuComponent.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from __future__ import annotations
2+
from abc import ABC, abstractmethod
3+
from typing import Iterator
4+
5+
6+
class UnsupportedOperationException(Exception): pass
7+
8+
class MenuComponent(ABC):
9+
10+
def add(self, menuComponent: MenuComponent) -> None:
11+
raise UnsupportedOperationException
12+
def remove(self, menuComponent: MenuComponent) -> None:
13+
raise UnsupportedOperationException
14+
def getChild(self, i: int) -> MenuComponent:
15+
raise UnsupportedOperationException
16+
17+
def getName(self) -> str:
18+
raise UnsupportedOperationException
19+
def getDescription(self) -> str:
20+
raise UnsupportedOperationException
21+
def getPrice(self) -> float:
22+
raise UnsupportedOperationException
23+
def isVegetarian(self) -> bool:
24+
raise UnsupportedOperationException
25+
26+
def print(self) -> None:
27+
raise UnsupportedOperationException

composite/menu/MenuItem.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from typing import Iterator
2+
3+
from MenuComponent import MenuComponent
4+
5+
6+
class MenuItem:
7+
name: str
8+
description: str
9+
vegetarian: bool
10+
price: float
11+
12+
def __init__(self, name: str, description: str, vegetarian: bool, price: float):
13+
self.name = name
14+
self.description = description
15+
self.vegetarian = vegetarian
16+
self.price = price
17+
18+
def getName(self) -> str:
19+
return self.name
20+
21+
def getDescription(self) -> str:
22+
return self.description
23+
24+
def getPrice(self) -> float:
25+
return self.price
26+
27+
def isVegetarian(self) -> bool:
28+
return self.vegetarian
29+
30+
def createIterator(self) -> Iterator[MenuComponent]:
31+
return NullIterator()
32+
33+
def print(self) -> None:
34+
print(f" {self.getName()}", end="")
35+
if self.isVegetarian():
36+
print(f"(v)", end="")
37+
print(f", {self.getPrice()}")
38+
print(f" -- {self.getDescription()}")

composite/menu/MenuTestDrive.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from Menu import Menu
2+
from MenuComponent import MenuComponent
3+
from MenuItem import MenuItem
4+
from Waitress import Waitress
5+
6+
7+
class MenuTestDrive:
8+
@staticmethod
9+
def main(*args):
10+
pancakeHouseMenu: MenuComponent = Menu("PANCAKE HOUSE MENU", "Breakfast")
11+
dinerMenu: MenuComponent = Menu("DINER MENU", "Lunch")
12+
cafeMenu: MenuComponent = Menu("CAFE MENU", "Dinner")
13+
dessertMenu: MenuComponent = Menu("DESSERT MENU", "Dessert of course!")
14+
coffeeMenu: MenuComponent = Menu("COFFEE MENU", "Stuff to go with your afternoon coffee")
15+
16+
allMenus: MenuComponent = Menu("ALL MENUS", "All menus combined")
17+
18+
allMenus.add(pancakeHouseMenu)
19+
allMenus.add(dinerMenu)
20+
allMenus.add(cafeMenu)
21+
22+
pancakeHouseMenu.add(MenuItem( "K&B's Pancake Breakfast", "Pancakes with scrambled eggs and toast", True, 2.99))
23+
pancakeHouseMenu.add(MenuItem( "Regular Pancake Breakfast", "Pancakes with fried eggs, sausage", False, 2.99))
24+
pancakeHouseMenu.add(MenuItem( "Blueberry Pancakes", "Pancakes made with fresh blueberries and blueberry syrup", True, 3.49))
25+
pancakeHouseMenu.add(MenuItem( "Waffles", "Waffles with your choice of blueberries or strawberries", True, 3.59))
26+
27+
dinerMenu.add(MenuItem( "Vegetarian BLT", "(Fakin') Bacon with lettuce & tomato on whole wheat", True, 2.99))
28+
dinerMenu.add(MenuItem( "BLT", "Bacon with lettuce & tomato on whole wheat", False, 2.99))
29+
dinerMenu.add(MenuItem( "Soup of the day", "A bowl of the soup of the day, with a side of potato salad", False, 3.29))
30+
dinerMenu.add(MenuItem( "Hot Dog", "A hot dog, with saurkraut, relish, onions, topped with cheese", False, 3.05))
31+
dinerMenu.add(MenuItem( "Steamed Veggies and Brown Rice", "A medly of steamed vegetables over brown rice", True, 3.99))
32+
dinerMenu.add(MenuItem( "Pasta", "Spaghetti with marinara sauce, and a slice of sourdough bread", True, 3.89))
33+
dinerMenu.add(dessertMenu)
34+
35+
dessertMenu.add(MenuItem( "Apple Pie", "Apple pie with a flakey crust, topped with vanilla icecream", True, 1.59))
36+
dessertMenu.add(MenuItem( "Cheesecake", "Creamy New York cheesecake, with a chocolate graham crust", True, 1.99))
37+
dessertMenu.add(MenuItem( "Sorbet", "A scoop of raspberry and a scoop of lime", True, 1.89))
38+
39+
cafeMenu.add(MenuItem( "Veggie Burger and Air Fries", "Veggie burger on a whole wheat bun, lettuce, tomato, and fries", True, 3.99))
40+
cafeMenu.add(MenuItem( "Soup of the day", "A cup of the soup of the day, with a side salad", False, 3.69))
41+
cafeMenu.add(MenuItem( "Burrito", "A large burrito, with whole pinto beans, salsa, guacamole", True, 4.29))
42+
43+
cafeMenu.add(coffeeMenu)
44+
45+
coffeeMenu.add(MenuItem("Coffee Cake", "Crumbly cake topped with cinnamon and walnuts", True, 1.59))
46+
coffeeMenu.add(MenuItem("Bagel", "Flavors include sesame, poppyseed, cinnamon raisin, pumpkin", False, 0.69))
47+
coffeeMenu.add(MenuItem("Biscotti", "Three almond or hazelnut biscotti cookies", True, 0.89))
48+
49+
waitress: Waitress = Waitress(allMenus)
50+
51+
waitress.printMenu() # book's example
52+
53+
if __name__ == "__main__":
54+
MenuTestDrive.main()

composite/menu/Waitress.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from typing import Iterator
2+
3+
from MenuComponent import MenuComponent
4+
5+
6+
class UnsupportedOperationException(Exception): pass
7+
8+
class Waitress:
9+
allMenus: MenuComponent
10+
11+
def __init__(self, allMenus: MenuComponent):
12+
self.allMenus = allMenus
13+
14+
def printMenu(self) -> None:
15+
self.allMenus.print()
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from collections import deque
2+
from typing import Deque, Iterator, List
3+
4+
from MenuComponent import MenuComponent
5+
6+
7+
class CompositeIterator(Iterator[MenuComponent]):
8+
stack: Deque[Iterator[MenuComponent]]
9+
10+
def __init__(self, iterator: Iterator[MenuComponent]):
11+
self.stack = deque()
12+
self.stack.append(iterator)
13+
14+
def __iter__(self):
15+
return self
16+
17+
# next
18+
def __next__(self) -> MenuComponent:
19+
if self.hasNext():
20+
iterator: Iterator[MenuComponent] = self.stack[-1] if len(self.stack) != 0 else None
21+
component: MenuComponent = next(iterator)
22+
self.stack.append(component.createIterator())
23+
return component
24+
else:
25+
raise StopIteration
26+
27+
def hasNext(self) -> bool:
28+
if len(self.stack) == 0:
29+
return False
30+
else:
31+
iterator: Iterator[MenuComponent] = self.stack[-1] if len(self.stack) != 0 else None
32+
if not iterator.hasNext():
33+
self.stack.pop()
34+
return self.hasNext()
35+
else:
36+
return True

composite/menuiterator/Menu.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from collections import UserList
2+
from typing import Iterator, List
3+
4+
from CompositeIterator import CompositeIterator
5+
from MenuComponent import MenuComponent
6+
7+
8+
class My_List(UserList):
9+
def __init__(self, data):
10+
super().__init__(data)
11+
self.position = 0
12+
13+
def __iter__(self):
14+
return self
15+
16+
def __next__(self):
17+
if self.hasNext():
18+
num = self.data[self.position]
19+
self.position += 1
20+
return num
21+
else:
22+
raise StopIteration
23+
24+
def hasNext(self):
25+
if self.position >= len(self.data):
26+
self.position = 0
27+
return False
28+
else:
29+
return True
30+
31+
class Menu(MenuComponent):
32+
iterator: Iterator[MenuComponent] = None
33+
menuComponents: List[MenuComponent]
34+
name: str
35+
description: str
36+
37+
def __init__(self, name: str, description: str):
38+
self.name = name
39+
self.description = description
40+
self.menuComponents = My_List([])
41+
42+
def add(self, menuComponent: MenuComponent) -> None:
43+
self.menuComponents.append(menuComponent)
44+
45+
def remove(self, menuComponent: MenuComponent) -> None:
46+
self.menuComponents.remove(menuComponent)
47+
48+
def getChild(self, i: int) -> MenuComponent:
49+
return self.menuComponents[i]
50+
51+
def getName(self) -> str:
52+
return self.name
53+
54+
def getDescription(self) -> str:
55+
return self.description
56+
57+
def createIterator(self) -> Iterator[MenuComponent]:
58+
if self.iterator == None:
59+
self.iterator = CompositeIterator(iter(self.menuComponents))
60+
return self.iterator
61+
62+
def print(self) -> None:
63+
print(f'\n{self.getName()}', end="")
64+
print(f", {self.getDescription()}")
65+
print("---------------------")
66+
67+
iterator: Iterator[MenuComponent] = iter(self.menuComponents)
68+
menuComponent: MenuComponent = next(iterator, None)
69+
while menuComponent != None:
70+
menuComponent.print()
71+
menuComponent: MenuComponent = next(iterator, None)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from __future__ import annotations
2+
from abc import ABC, abstractmethod
3+
from typing import Iterator
4+
5+
6+
class UnsupportedOperationException(Exception): pass
7+
8+
class MenuComponent(ABC):
9+
10+
def add(self, menuComponent: MenuComponent) -> None:
11+
raise UnsupportedOperationException
12+
def remove(self, menuComponent: MenuComponent) -> None:
13+
raise UnsupportedOperationException
14+
def getChild(self, i: int) -> MenuComponent:
15+
raise UnsupportedOperationException
16+
def getName(self) -> str:
17+
raise UnsupportedOperationException
18+
def getDescription(self) -> str:
19+
raise UnsupportedOperationException
20+
def getPrice(self) -> float:
21+
raise UnsupportedOperationException
22+
def isVegetarian(self) -> bool:
23+
raise UnsupportedOperationException
24+
@abstractmethod
25+
def createIterator(self) -> Iterator[MenuComponent]:
26+
raise NotImplementedError
27+
def print(self) -> None:
28+
raise UnsupportedOperationException

composite/menuiterator/MenuItem.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from typing import Iterator
2+
3+
from MenuComponent import MenuComponent
4+
from NullIterator import NullIterator
5+
6+
7+
class MenuItem:
8+
9+
name: str
10+
description: str
11+
vegetarian: bool
12+
price: float
13+
14+
def __init__(self, name: str, description: str, vegetarian: bool, price: float):
15+
self.name = name
16+
self.description = description
17+
self.vegetarian = vegetarian
18+
self.price = price
19+
20+
def getName(self) -> str:
21+
return self.name
22+
23+
def getDescription(self) -> str:
24+
return self.description
25+
26+
def getPrice(self) -> float:
27+
return self.price
28+
29+
def isVegetarian(self) -> bool:
30+
return self.vegetarian
31+
32+
def createIterator(self) -> Iterator[MenuComponent]:
33+
return NullIterator()
34+
35+
def print(self) -> None:
36+
print(f" {self.getName()}", end="")
37+
if self.isVegetarian():
38+
print(f"(v)", end="")
39+
print(f", {self.getPrice()}")
40+
print(f" -- {self.getDescription()}")

0 commit comments

Comments
 (0)