본문 바로가기
Programming/프로그래밍 이론

[디자인 패턴] 복합체 패턴 (Composite pattern)

by SpiralMoon 2019. 3. 12.
반응형

 주제 : 복합체 패턴 (Composite pattern).


구조 패턴의 하나.


복합 객체 그룹(전체)과 단일 객체를 동일하게 취급하거나 다룰 수 있게 해주는 방식으로, 재귀적 특성을 띄며 트리(tree) 구조에 가까운 패턴이다.


 구조


복합체 패턴의 구조는 크게 4가지로 분류된다.

첫 번째 : 컴포넌트 인터페이스(추상클래스도 가능) : Leaf와 Composite를 같은 타입으로 취급하기 위한 인터페이스로써, Leaf와 Composite가 동일하게 가져야 할 함수를 여기에 선언한다.

 두 번째 : 리프 클래스 : 단일 객체를 표현할 클래스로, 그룹의 구성원 역할을 하며 트리구조로 따지면 가장 밑단에 존재하는 나뭇잎 역할을 한다고 보면 된다.

 세 번째 : 컴포지트(복합체) 클래스 : 복합 객체 그룹을 표현할 클래스로, 자식으로 여러개의 Component 타입 멤버를 수용할 수 있도록 구현되어야 한다.


▶  예시


PPT의 도형이나 그룹을 움직여본다는 상황이라고 했을 때, 아래 예시처럼 표현할 수 있다.

아래 예시는 C#을 기반으로 작성하였다.

public abstract class GraphicComponent
{
    // 기본 X 축
    public int X;

    // 기본 Y 축
    public int Y;

    // 좌표 이동
    public abstract void Move(int x, int y);
}

도형과 그룹을 동일한 그래픽 타입으로 취급하도록 Component 인터페이스 혹은 추상클래스를 정의한다.

이번 예시는 도형을 움직인다는 상황이기 때문에 좌표값을 표현할 멤버변수가 필요하여 추상클래스로 정의하였다.

public class Shape : GraphicComponent
{
    public Shape(int x, int y)
    {
        X = x;
        Y = y;
    }

    public override void Move(int x, int y)
    {
        X += x;
        Y += y;

        Console.WriteLine($"도형 {GetHashCode()}는 x={X}, y={Y} 좌표로 이동하였습니다.");
    }
}

그래픽 컴포넌트를 상속하는 도형 클래스를 정의하였다. Move(int x, int y) 함수를 오버라이딩하여 좌표값이 실제로 반영되게 했다.

using System;
using System.Collections.Generic;

namespace CompositePattern
{
    public class GraphicGroup : GraphicComponent
    {
        private List _graphicComponents;

        public GraphicGroup(int x, int y)
        {
            _graphicComponents = new List();
            X = x;
            Y = y;
        }

        public override void Move(int x, int y)
        {
            X += x;
            Y += y;

            Console.WriteLine($"그룹 {GetHashCode()}는 x={X}, y={Y} 좌표로 이동하였습니다.");

            // 모든 하위 자식 도형들의 좌표를 이동시켜줌.
            // 그룹 안에 또 다른 그룹이 있는 경우 재귀호출이 됨.
            foreach (GraphicComponent g in _graphicComponents)
            {
                g.Move(x, y);
            }
        }

        // 추가
        public void Add(GraphicComponent graphicComponent)
        {
            _graphicComponents.Add(graphicComponent);
        }

        // 삭제
        public void Remove (GraphicComponent graphicComponent)
        {
            _graphicComponents.Remove(graphicComponent);
        }

        // 인덱스에 해당하는 자식을 반환
        public GraphicComponent GetChild(int index)
        {
            return _graphicComponents[index];
        }
    }
}

그래픽 컴포넌트를 상속하는 그래픽 그룹 클래스를 정의하였다.

그룹의 자식으로 또 다른 그래픽 컴포넌트들을 수용할 수 있고, Move( )가 호출되면 모든 자식의 좌표까지 이동할 수 있도록 재귀호출을 사용하였다.

즉, 그래픽 그룹 안에 도형이나 또 다른 그래픽 그룹을 집어넣어도 일관되게 제어할 수 있다.

// 최상위 그룹 생성
var rootGraphicGroup = new GraphicGroup(0, 0);
// 도형 2개 생성
var shape1 = new Shape(10, 10);
var shape2 = new Shape(5, 20);

// 그룹에 도형 추가
rootGraphicGroup.Add(shape1);
rootGraphicGroup.Add(shape2);

// 서브 그룹을 생성하고 도형 추가
var subGraphicGroup = new GraphicGroup(100, 100);
var shape3 = new Shape(0, 0);
var shape4 = new Shape(50, 75);
subGraphicGroup.Add(shape3);
subGraphicGroup.Add(shape4);

// 최상위 그룹에 서브 그룹을 추가
rootGraphicGroup.Add(subGraphicGroup);

// 그룹째로 이동
rootGraphicGroup.Move(10, 10);

실제 이용 소스코드이다. 그룹에 도형과 또 다른 그룹을 넣어주고 X Y 축 각각 10씩 이동시켰다.

최상위 그룹을 이동시키자 하위 모든 자식들이 이동되었다.



그림으로 설명하자면 위의 소스코드는 이 그림의 트리구조로 구현됬다고 할 수 있다.


▶  장점

▷ 그룹핑 : 단일 객체와 복합 객체(그룹)를 동일하게 여기기 때문에 묶어서 연산하거나 관리할 때 편하다.


▶  단점

▷ 디버깅 어려움 : 재귀호출의 특징 상 트리의 Depth가 깊어질 수록 라인 단위의 디버깅에 어려움이 생긴다.



반응형

댓글