Composite는 객체를 트리 구조로 구성한 다음 개별 객체인것처럼 이러한 구조로 작업 할 수 있는 구조적 Design pattern입니다.
Composite pattern을 사용하는 것은 앱의 핵심 모델을 트리로 표현 할 수 있는 경우에만 의미가 있습니다.
예를 들어 Products
과 Boxes
라는 두 가지 유형의 개체가 있다고 가정합니다. 상자에는 여거 개의 Products
과 여러 개의 작은 Boxes
가 포함될 수 있습니다. 이 작은 Boxes
에는 일부 Products
또는 더 작은 Boxes
등도 담을 수 있습니다.
이러한 클래스를 사용하는 주문 시스템을 만들기로 결정했다고 가정해 보겠습니다. 주문에는 포장이 없는 단순한 Product와 Product로 재워진 Box 및 기타 Box가 포함될 수 있습니다. 그러한 주문의 총 가격을 어떻게 결정 하시겠습니까?
직접 접근 방식을 시도해 볼 수 있습니다. 모든 상자를 풀고 모든 제품을 살표본 다음 합계를 계산하십시오. 현실 세계에서는 가능 하겠지만 프로그램에서는 루프를 실행하는 것만큼 간단하지 않습니다. 진행중인 Products
및 Boxes
의 클래스, 상자의 중첩 수준 및 기타 불쾌한 세부 정보를 미리 알아야 합니다. 이 모든 것이 직접적인 접근 방식을 너무 어색하거나 불가능하게 만듭니다.
Composite pattern은 총 가격을 계산하는 방법을 선언하는 공통 Interface를 통해 Products
및 Boxex
로 작업 할 것을 제안합니다.
How would this method work? Products의 경우 단순히 Products 가격을 반환합니다. Boxes의 경우 Boxes에 포함 된 각 항목을 살펴보고 가격을 물어 본 다음 이 Boxes의 합계를 반환합니다. 이러한 항목 중 하나가 더 작은 Box인 경우 모든 내부 구성 요소의 가격이 계산 될 때까지 해당 Box도 내용물을 넘기기 시작합니다. Box는 포장 비용과 같은 최종 가격에 추가 비용을 추가 할 수도 있습니다.
이 접근 방식의 가장 큰 이점은 Tree를 구성하는 객체의 구체적인 클래스에 대해 신경 쓸 필요가 없다는 것입니다. 물건이 단순한 제품인지 정교한 상자인지 알 필요가 없습니다. 공통 Interface를 통해 동일하게 처리 할 수 있습니다. 메서드를 호출하면 개체 자체가 Tree 아래로 요청을 전달합니다.
대부분 국가의 군대는 계층 구조로 구성됩니다. 군대는 여러 부서로 구성됩니다. 사단은 여단의 집합이고 여단은 소대로 구성되며 분대로 나눌 수 있습니다. 마지막으로 분대는 실제 군인의 작은 그룹입니다. 명령은 계층 구조의 맨 위에 주어지며 모든 병사가 수행해야 할 작업을 알 때까지 각 레벨로 전달됩니다.
1. Component Interface는 Tree의 단순 요소와 복잡한 요소 모두에 공통적인 작업을 설명합니다.
2. Leaf는 하위 요소가 없는 나무의 기본 요소입니다.
일반적으로 Leaf 구성 요소는 작업을 위임 할 사람이 없기 때문에 대부분의 실제 작업을 수행합니다.
3. Container(aka composite)는 sub-elements가 있는 element입니다. Container는 작식의 구체적인 클래스를 알지 못합니다. Component Interface를 통해서만 모든 하위 요소와 함께 작동합니다.
요청을 받으면 Container는 작업을 하위 요소에 위임하고 중간 결과를 처리 한 다음 최종 결과를 Client에 반환합니다.
4. Client는 Component Interface를 통해 모든 요소와 함께 작동합니다. 결과적으로 Client는 Tree의 단순하거나 복잡한 요소 모두에서 동일한 방식으로 작업 할 수 있습니다.
이 예제에서 Composite pattern을 사용하면 그래픽 편집기에서 기하학적 모양 스택을 구현할 수 있습니다.
CompoundGraphic
클래스는 다른 복합 모양을 포함하여 여러 하위 모양을 구성 할 수 있는 Container입니다. 복합 모양은 단순 모양과 동일한 방법을 사용합니다. 그러나 자체적으로 무언가를 하는 대신 복합 형태는 요청을 모든 자식에게 재귀적으로 전달하고 결과를 "합산"합니다.
클라이언트 코드는 모든 Shapes 클래스에 공통된 단일 Interface를 통해 모든 Shapes에서 작동합니다. 따라서 클라이언트는 간단한 모양으로 작동하는지 복합 모양으로 작동하는지 알지 못합니다. 클라이언트는 해당 구조를 형성하는 구체적인 클래스에 연결되지 않고도 매우 복잡한 개체 구조로 작업 할 수 있습니다.
// The component interface declares common operations for both
// simple and complex objects of a composition.
interface Graphic is
method move(x, y)
method draw()
// The leaf class represents end objects of a composition. A
// leaf object can't have any sub-objects. Usually, it's leaf
// objects that do the actual work, while composite objects only
// delegate to their sub-components.
class Dot implements Graphic is
field x, y
constructor Dot(x, y) { ... }
method move(x, y) is
this.x += x, this.y += y
method draw() is
// Draw a dot at X and Y.
// All component classes can extend other components.
class Circle extends Dot is
field radius
constructor Circle(x, y, radius) { ... }
method draw() is
// Draw a circle at X and Y with radius R.
// The composite class represents complex components that may
// have children. Composite objects usually delegate the actual
// work to their children and then "sum up" the result.
class CompoundGraphic implements Graphic is
field children: array of Graphic
// A composite object can add or remove other components
// (both simple or complex) to or from its child list.
method add(child: Graphic) is
// Add a child to the array of children.
method remove(child: Graphic) is
// Remove a child from the array of children.
method move(x, y) is
foreach (child in children) do
child.move(x, y)
// A composite executes its primary logic in a particular
// way. It traverses recursively through all its children,
// collecting and summing up their results. Since the
// composite's children pass these calls to their own
// children and so forth, the whole object tree is traversed
// as a result.
method draw() is
// 1. For each child component:
// - Draw the component.
// - Update the bounding rectangle.
// 2. Draw a dashed rectangle using the bounding
// coordinates.
// The client code works with all the components via their base
// interface. This way the client code can support simple leaf
// components as well as complex composites.
class ImageEditor is
field all: CompoundGraphic
method load() is
all = new CompoundGraphic()
all.add(new Dot(1, 2))
all.add(new Circle(5, 3, 10))
// ...
// Combine selected components into one complex composite
// component.
method groupSelected(components: array of Graphic) is
group = new CompoundGraphic()
foreach (component in components) do
group.add(component)
all.remove(component)
all.add(group)
// All components will be drawn.
all.draw()
Tree와 같은 개체 구조를 구현해야 할 때 Composite pattern을 사용합니다.
Composite pattern은 공통 Interface를 공유하는 두가지 기본 요소 유형을 제공합니다. Container는 leaves와 complex containers로 구성 될 수 있습니다. 이를 통해 Tree와 유사한 중첩 된 재귀 객체 구조를 구성 할 수 있습니다.
클라이언트 코드가 단순 요소와 복잡한 요소를 모두 균일하게 처리하도록 하려면 Composite pattern을 사용하십시오.
Composite pattern으로 정의 된 모든 요소는 공통 Interface를 공유합니다. 이 Interface를 사용하면 클라이언트는 작업하는 객체의 구체적인 클래스에 대해 걱정할 필요가 없습니다.
O 복잡한 트리 구조로 더 편리하게 작업 할 수 있습니다. 다형성과 재귀를 유리하게 사용하십시오.
O 개방 / 폐쇄 원칙. 이제 개체 트리와 함께 작동하는 기존 코드를 손상시키지 않고 앱에 새 요소 유형을 도입 할 수 있습니다.
X 기능이 너무 많이 다른 클래스에 대한 공통 인터페이스를 제공하는 것은 어려울 수 있습니다. 특정 시나리오에서는 구성 요소 인터페이스를 지나치게 일반화하여 이해하기 어렵게 만들어야합니다.