컴포지트: 하나와 묶음을 같은 방식으로 다루기
장바구니에는 낱개 상품도 있고 여러 상품이 담긴 묶음도 있습니다. 전체 가격을 구할 때마다 모든 종류를 일일이 검사하는 대신 각 대상이 자신의 합계를 답하게 만들 수 있습니다.
잎은 값을, 묶음은 자식들의 합을 답합니다
class Item:
def __init__(self, price):
self.price = price
def total(self):
return self.price
class Bundle:
def __init__(self, children):
self.children = list(children)
def total(self):
return sum(child.total() for child in self.children)
basket = Bundle([Item(1000), Bundle([Item(2000), Item(500)])])
print(basket.total())
출력은 3500입니다. Item은 자기 가격을 반환하고 Bundle은 자식의 total을 호출해 더합니다. 묶음 안에 또 다른 묶음이 있어도 같은 방식으로 처리합니다. 이것이 컴포지트 패턴의 기본 구조입니다.
공통 인터페이스가 있다고 모든 기능을 똑같이 제공해야 하는 것은 아닙니다. 자식 추가는 묶음에만 의미가 있습니다. 잎에도 무의미한 add를 강제로 넣는 대신 각 역할에 맞는 인터페이스를 설계할 수 있습니다.
트리라는 가정을 지킵니다
묶음이 자기 자신을 자식으로 갖거나 조상을 다시 포함하면 순환이 생겨 단순 재귀가 끝나지 않을 수 있습니다. 공유된 상품을 두 묶음에 넣을 때 두 번 더하는 것이 맞는지도 정해야 합니다. 구조가 크고 깊다면 재귀 깊이와 반복 계산 비용도 고려합니다.
확인 문제
같은 상품 객체를 한 묶음 안에 두 번 넣으면 예제는 가격을 몇 번 더할까요? 이것은 반드시 버그일까요?
해설 보기
두 번 더합니다. 두 개를 구매한다는 뜻이면 맞지만, 유일한 파일들의 용량을 구하는 문제라면 중복 계산일 수 있습니다. 패턴은 구조를 제공하며, 중복의 의미는 문제의 규칙으로 정해야 합니다.