개발/디자인패턴

[Java][디자인 패턴] 9. 복합체 패턴 (Composite Pattern)

nova_dev 2022. 3. 8. 23:59
반응형

디자인패턴
[Java][디자인 패턴] 9. 복합체 패턴 (Composite Pattern)

복합체 패턴은 객체 간의 계층적 구조화를 통해 객체를 확장하는 패턴이다. 복합체는 재귀적으로 결합된 계층화된 트리 구조의 객체이다.

 

 복합 객체란?

  • 복합 객체는 객체가 또 다른 객체를 포함하는 것
  • 복합적인 객체 관계를 복합화 또는 집단화라고 한다.
  • 강력한 결합 구조를 가진 상속과 달리 느슨한 결합을 가지고 있으며, 의존체 주입 방식을 사용한다.

 

 복합체 패턴이란?

  • 복합채 패턴은 분할 디자인 패턴의 하나이다.
  • 복합체 패턴을 이용하면 객체의 상위, 하위 체계를 파악할 수 있고 일대일, 다대일을 처리하는 데도 매우 유용하다. 하나의 객체를 호출하면 서브로 갖고 있는 자식의 객체 메서드를 호출할 수도 있다.

 

 복합체 패턴은 언제 사용하면 좋을까?

트리 모양을 가진 구성 요소들

- 회사와 부서, 팀과 같이 그룹과 해당 그룹에 속한 하위 그룹이 있는 계층(트리) 구조 형태의 관리
- 세트 상품 (다른 세트 상품의 일부가 될 수 있음)
- 회원 관리, 이메일 전송 (그룹으로 묶인 회원을 대상으로 메일을 보낸다)
- 폴더와 파일 구조와 같은 트리 형태
- 파워포인트의 도형 그룹, 여러 도형을 한꺼번에 바꿀 경우

 

 

 

 복합체 패턴의 구성 요소

복합체 패턴 구성요소

  • Component
    • 컴포넌트 역할을 수행하는 추상 클래스
    • 노드인 Composite와 마지막 노드인 Leaf에 공통으로 적용된다.
    • Composite와 leaf는 동일한 처리를 위해 Component 추상 클래스를 상속받는다.
  • Composite
    • 복합체 패턴은 다른 복합체 패턴을 포함할 수도 있고 마지막 노드가 될 수도 있다
  • Leaf
    • 복합체는 계층적 트리 구조로 되어있다
    • 노드의 제일 마지막 객체
  • Client
    • 복합체를 호출하는 클래스

 

 복합체 패턴 예제 코드 구조

복합체 패턴 예제 코드 구조

  • Component - Shape 클래스 (Leaf와 Composite에 공통으로 적용할 클래스)
  • Composite - ShapeComposite (Shape List를 가지고 그룹 전체에 공통된 연산을 적용할 클래스
  • Leaf - Triangle, Circle (Component를 구현하고 있는 공통적인 동작을 수행하는 Leaf 클래스)
  • Client - TestCode

복합체 패턴 구조에 해당하는 예시 코드로는, 도형의 색을 변경하고 옮기고 표현하는 공통적인 동작을 수행하는 Shape 인터페이스를 만들고, 각 도형들을 그룹화해서 관리할 수 있는 ShapeComposite 클래스를 통해 객체를 관리해보려 한다.

 

 복합체 패턴 코드

1. Shape 인터페이스

public interface Shape {
    void draw(String fillColor);

    void move(Integer x, Integer y);

    void print();

    default String getDefaultColor() {
        return "BLUE";
    }
}

2. Shape 인터페이스를 구현하고 있는 Circle, Triangle 클래스

public class Circle implements Shape {
    private final Point point;
    private String color;

    public Circle() {
        this.point = new Point();
        this.color = getDefaultColor();
    }

    @Override
    public void draw(String fillColor) {
        this.color = fillColor;
    }

    @Override
    public void move(Integer x, Integer y) {
        point.move(x, y);
    }

    @Override
    public void print() {
        System.out.println("[Circle][" + color + "] " + point.toString());
    }
}
public class Triangle implements Shape {
    private final Point point;
    private String color;

    public Triangle() {
        this.point = new Point();
        this.color = getDefaultColor();
    }

    @Override
    public void draw(String fillColor) {
        this.color = fillColor;
    }

    @Override
    public void move(Integer x, Integer y) {
        point.move(x, y);
    }

    @Override
    public void print() {
        System.out.println("[Triangle][" + color + "] " + point.toString());
    }

}

3. Shape의 좌표 값을 가지고 있는 Point 클래스

public class Point {
    private Integer x;
    private Integer y;

    public Point(Integer x, Integer y) {
        this.x = x;
        this.y = y;
    }

    public Point() {
        this.x = 0;
        this.y = 0;
    }

    public void move(Integer x, Integer y) {
        this.x = this.x + x;
        this.y = this.y + y;
    }

    public Integer getX() {
        return x;
    }

    public Integer getY() {
        return y;
    }

    @Override
    public String toString() {
        return "Point{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }
}

4. Shape들을 그룹화해서 한꺼번에 관리하는 ShapeComposite 클래스

public class ShapeComposite implements Shape {

    private final List<Shape> shapeList = new ArrayList<>();

    @Override
    public void draw(String fillColor) {
        for (Shape shape : shapeList) {
            shape.draw(fillColor);
        }
    }

    @Override
    public void move(Integer x, Integer y) {
        for (Shape shape : shapeList) {
            shape.move(x, y);
        }
    }

    @Override
    public void print() {
        for (Shape shape : shapeList) {
            shape.print();
        }
    }
    
    public void add(Shape shape) {
        shapeList.add(shape);
    }

    public void remove(Shape shape) {
        shapeList.remove(shape);
    }

    public void clear() {
        System.out.println("clear");
        shapeList.clear();
    }
}

5. ShapeComposite 클래스의 테스트 코드

class ShapeCompositeTest {
    @Test
    @DisplayName("도형을 그리고 한꺼번에 옮긴다")
    void drawShape() {
        Shape triangle = new Triangle();
        Shape triangle2 = new Triangle();
        Shape circle = new Circle();

        ShapeComposite shapeComposite = new ShapeComposite();
        shapeComposite.add(triangle);
        shapeComposite.add(triangle2);
        shapeComposite.add(circle);

        shapeComposite.print();
        shapeComposite.draw("RED");
        shapeComposite.print();
        shapeComposite.move(1, 1);
        shapeComposite.print();
        shapeComposite.move(2, -3);
        shapeComposite.print();
    }
}

결과 값

[Triangle][BLUE] Point{x=0, y=0}
[Triangle][BLUE] Point{x=0, y=0}
[Circle][BLUE] Point{x=0, y=0}
[Triangle][RED] Point{x=0, y=0}
[Triangle][RED] Point{x=0, y=0}
[Circle][RED] Point{x=0, y=0}
[Triangle][RED] Point{x=1, y=1}
[Triangle][RED] Point{x=1, y=1}
[Circle][RED] Point{x=1, y=1}
[Triangle][RED] Point{x=3, y=-2}
[Triangle][RED] Point{x=3, y=-2}
[Circle][RED] Point{x=3, y=-2}

 복합체 패턴의 장점과 단점

장점

  • 단일 객체와 복합 객체 (그룹)을 동일하게 여기기 때문에 묶어서 연산하거나 관리할 때 편하다
  • 객체에 따라 다르게 처리하지 않고 동일하게 취급해야 할 때 사용
  • 투명성을 이용해 클라이언트 사용을 단순화할 수 있음. (if문을 사용하지 않고도 Composite와 Leaf를 판단 가능)

단점

  • 재귀 호출의 특징 상 트리의 Depth가 길어질수록 라인 단위의 디버깅에 어려움이 생긴다.
  • 복합체 패턴은 수평적, 수직적 모든 방향으로 객체를 확장할 수 있으나 수평적 방향으로만 확장하도록 Leaf를 제한한 Composite를 만들기는 어렵다.

 Github 코드

Github 예제 코드 링크

Github 예제 테스트 코드 링크

참고 자료

 

 

 

 

 

반응형