-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathorg.py
More file actions
51 lines (32 loc) · 1.46 KB
/
Copy pathorg.py
File metadata and controls
51 lines (32 loc) · 1.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
"""Departments hold teams hold people; one ``total()`` serves every level.
The interface-honesty rule in practice: ``Employee`` is a frozen leaf with no
child management — only ``Department`` (a ``Composite``) can ``add``/``remove``.
Both answer ``total()``, so headcount and cost roll up through any nesting
without ever asking a node what kind it is.
"""
from __future__ import annotations
from collections.abc import Iterable
from dataclasses import dataclass
from patterns.structural.composite.pattern import Composite, HasTotal
@dataclass(frozen=True)
class OrgMetrics:
"""The rollup value: both measures travel up the tree together."""
headcount: int
annual_cost: int
def __add__(self, other: OrgMetrics) -> OrgMetrics:
return OrgMetrics(self.headcount + other.headcount, self.annual_cost + other.annual_cost)
ZERO = OrgMetrics(0, 0)
def combine(parts: Iterable[OrgMetrics]) -> OrgMetrics:
return sum(parts, start=ZERO)
@dataclass(frozen=True)
class Employee:
"""A leaf. No ``add()`` — people honestly cannot hold reports here."""
name: str
salary: int
def total(self) -> OrgMetrics:
return OrgMetrics(headcount=1, annual_cost=self.salary)
class Department(Composite[OrgMetrics]):
"""A named container node; child management lives here, where it belongs."""
def __init__(self, name: str, members: Iterable[HasTotal[OrgMetrics]] = ()) -> None:
super().__init__(combine, members)
self.name = name