7.1. Composite¶
EN: Composite
PL: Kompozyt
Type: object
7.1.1. Pattern¶
Represent a hierarchy of objects
Groups (and subgroups) objects in Keynote
Files in a Folder; when you move folder you also move files
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 hierarchical relationships around.
makes it easier for you to add new kinds of components
conform to the Single Responsibility Principle in the way that it separates the aggregation of objects from the features of the object.

7.1.2. Problem¶

from typing import Self
from dataclasses import dataclass, field
@dataclass
class Shape:
name: str
def render(self) -> None:
print(f'Render {self.name}')
def move(self) -> None:
print(f'Move {self.name}')
@dataclass
class Group:
objects: list[Shape | Self] = field(default_factory=list)
def add(self, obj: Shape | Self) -> None:
self.objects.append(obj)
def render(self) -> None:
for obj in self.objects:
obj.render()
def move(self) -> None:
for obj in self.objects:
obj.move()
if __name__ == '__main__':
group1 = Group()
group1.add(Shape('square'))
group1.add(Shape('rectangle'))
group2 = Group()
group2.add(Shape('circle'))
group2.add(Shape('ellipse'))
everything = Group()
everything.add(group1)
everything.add(group2)
everything.render()
# Render square
# Render rectangle
# Render circle
# Render ellipse
everything.move()
# Move square
# Move rectangle
# Move circle
# Move ellipse
7.1.3. Solution¶

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
class Component(ABC):
@abstractmethod
def render(self) -> None:
pass
@abstractmethod
def move(self) -> None:
pass
@dataclass
class Shape(Component):
name: str
def move(self) -> None:
print(f'Move {self.name}')
def render(self) -> None:
print(f'Render {self.name}')
@dataclass
class Group(Component):
components: list[Component] = field(default_factory=list)
def add(self, component: Component) -> None:
self.components.append(component)
def render(self) -> None:
for component in self.components:
component.render()
def move(self) -> None:
for component in self.components:
component.move()
if __name__ == '__main__':
group1 = Group()
group1.add(Shape('square'))
group1.add(Shape('rectangle'))
group2 = Group()
group2.add(Shape('circle'))
group2.add(Shape('ellipse'))
everything = Group()
everything.add(group1)
everything.add(group2)
everything.render()
# Render square
# Render rectangle
# Render circle
# Render ellipse
everything.move()
# Move square
# Move rectangle
# Move circle
# Move ellipse
7.1.4. Assignments¶
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
class Shape(ABC):
@abstractmethod
def print(self):
pass
class Ellipse(Shape):
def print(self):
print('Ellipse')
class Circle(Shape):
def print(self):
print('Circle')
@dataclass
class Group(Shape):
children: list = field(default_factory=list)
def add(self, graphic):
self.children.append(graphic)
def print(self):
for children in self.children:
children.print()
if __name__ == '__main__':
group1 = Group()
group1.add(Ellipse())
group2 = Group()
group2.add(Circle())
group2.add(group1)
group2.print()