StructuralPythonverifiedVerified
Composite Pattern in Python
Composes objects into tree structures to represent part-whole hierarchies, letting clients treat individual objects and compositions uniformly.
How to Implement the Composite Pattern in Python
1Step 1: Define the component interface for uniform treatment
from __future__ import annotations
from abc import ABC, abstractmethod
class Component(ABC):
@abstractmethod
def operation(self) -> str: ...
def is_composite(self) -> bool:
return False2Step 2: Implement Leaf and Composite node types
class Leaf(Component):
def __init__(self, name: str) -> None:
self._name = name
def operation(self) -> str:
return f"Leaf({self._name})"
class Composite(Component):
def __init__(self) -> None:
self._children: list[Component] = []
def add(self, component: Component) -> None:
self._children.append(component)
def remove(self, component: Component) -> None:
self._children.remove(component)
def is_composite(self) -> bool:
return True
def operation(self) -> str:
results = "+".join(c.operation() for c in self._children)
return f"Branch({results})"3Step 3: Build a tree and traverse it uniformly
tree = Composite()
branch = Composite()
branch.add(Leaf("1"))
branch.add(Leaf("2"))
tree.add(branch)
tree.add(Leaf("3"))
print(tree.operation())
# Branch(Branch(Leaf(1)+Leaf(2))+Leaf(3))"""File-system tree where files and directories share a uniform Node interface."""
from __future__ import annotations
from abc import ABC, abstractmethod
# [step] Define the FsNode interface
class FsNode(ABC):
@property
@abstractmethod
def name(self) -> str: ...
@abstractmethod
def size(self) -> int: ...
@abstractmethod
def print_tree(self, indent: str = "") -> None: ...
# [step] Implement the File leaf node
class File(FsNode):
def __init__(self, name: str, bytes_: int) -> None:
self._name = name
self._bytes = bytes_
@property
def name(self) -> str:
return self._name
def size(self) -> int:
return self._bytes
def print_tree(self, indent: str = "") -> None:
print(f"{indent} {self._name} ({self._bytes} B)")
# [step] Implement the Directory composite node
class Directory(FsNode):
def __init__(self, name: str) -> None:
self._name = name
self._children: list[FsNode] = []
@property
def name(self) -> str:
return self._name
def add(self, node: FsNode) -> "Directory":
self._children.append(node)
return self
def remove(self, name: str) -> bool:
before = len(self._children)
self._children = [c for c in self._children if c.name != name]
return len(self._children) < before
def find(self, name: str) -> FsNode | None:
for child in self._children:
if child.name == name:
return child
if isinstance(child, Directory):
found = child.find(name)
if found:
return found
return None
def size(self) -> int:
return sum(c.size() for c in self._children)
def print_tree(self, indent: str = "") -> None:
print(f"{indent} {self._name}/ ({self.size()} B)")
for child in self._children:
child.print_tree(indent + " ")
# [step] Build and display a project tree
root = (
Directory("project")
.add(
Directory("src")
.add(File("index.ts", 1200))
.add(File("utils.ts", 800))
)
.add(
Directory("tests")
.add(File("index.test.ts", 600))
)
.add(File("package.json", 300))
)
root.print_tree()
print("Total size:", root.size(), "B")Composite Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Composite Pattern in the Real World
“A company’s org chart is a composite structure. An individual employee (leaf) has a salary and a name. A department (composite) also has a name and a budget—calculated by summing the salaries of all its members, which may themselves be other departments. HR can call ‘get budget’ on the CEO’s node and the entire tree is summed recursively.”