본문 바로가기

CS ﹒ Algorithm/DesignPatterns

디자인 패턴 (8) Composite Pattern : 컴포짓 패턴

* 여러 자료들을 참고했으나 제가 Composite Pattern에 대해 명확하게 이해한 것인지 확신하지 못하겠습니다.

개인 학습용 자료라 생각하고 정보가 필요하신 분들은 다른 곳을 참고하시길 바랍니다.

https://www.baeldung.com/java-composite-pattern

 

Composite Design pattern in Java | Baeldung

Have a look at how to implement the composite design pattern in Java.

www.baeldung.com

https://stackoverflow.com/questions/5334353/when-should-i-use-composite-design-pattern

 

When should I use composite design pattern?

I don't understand when I should use composite design pattern. What kinds of benefits will I get from this design pattern? I visited this website but it only tells me about the structure of the des...

stackoverflow.com

 

Composite Pattern?

 

컴포짓 패턴이란 그룹 전체와 개별 객체를 동일하게 처리할 수 있는 패턴을 의미한다.

이는 즉 클라이언트 입장에서는 전체와 부분을 모두 동일한 컴포넌트로 인식할 수 있는 것이다.

 

위 그림을 보면 알겠지만 Composite Pattern으로 설계하면 트리형 계층 구조가 만들어진다.

말로만 들으면 이해가 쉽지 않으니 간단한 코드로 알아보자.

 

 

 

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
public class BrushProgram2 {
    String name = "BrushProgram2";
 
    public void execute() {
        System.out.println(this.name + " 실행");
    }
}
 
public class CaptureProgram2 {
    String name = "캡처 프로그램";
 
    public void execute() {
        System.out.println(this.name + " 실행");
    }
}
 
public class LeagueOfLegends2 {
    String name = "리그오브레전드";
 
    public void execute() {
        System.out.println(this.name + " 실행");
    }
}
 
public class Starcraft2 {
    String name = "스타크래프트";
 
    public void execute() {
        System.out.println(this.name + " 실행");
    }
}
 
public class example2_App {
    public static void main(String[] args) {
        BrushProgram2 brushProgram2 = new BrushProgram2();
        CaptureProgram2 captureProgram2 = new CaptureProgram2();
        LeagueOfLegends2 leagueOfLegends2 = new LeagueOfLegends2();
        Starcraft2 starcraft2 = new Starcraft2();
 
        brushProgram2.execute();
        captureProgram2.execute();
        leagueOfLegends2.execute();
        starcraft2.execute();
}
}
 
 
cs

 

 

 

위와 같은 별 쓸모는 없는 클래스들이 있다고 가정해보자.

우리는 해당 클래스들에서 쉽게 공통 분모를 찾을 수 있다.

운영체제의 관점에서 봤을 때 해당 클래스들을 한 단계 추상화하면 "File"이라는 이름으로 추상화할 수 있을 것이다.

 

 

 

 

 

1
2
3
4
public interface File {
    void execute();
}
 
cs

 

 

볼품 없지만 아무튼 File이라는 인터페이스를 만들었다.

그런데 아직 약간 아쉽다, 좀 더 세분화된 추상화를 해보자.

캡처 프로그램과 브러쉬 프로그램은 유틸리티, 리그오브레전드와 스타크래프트는 게임으로 분류하면 어떨까?

 

그런데 우리가 만드는 건 Composite Design Pattern이라는 사실을 잊지 말자.

클라이언트단에서 File 하나로 모든 걸 사용 가능하게 할 것이기 때문에 File 밑으로 계층을 만들어줘야 한다.

 

 

 

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
public class Game implements File{
    private String name;
 
    public Game(String name) {
        this.name = name;
    }
 
    @Override
    public void execute() {
        System.out.println(this.name + " 실행");
    }
}
 
 
public class Utility implements File {
    private String file;
 
    public Utility(String file) {
        this.file = file;
    }
 
    @Override
    public void execute() {
        System.out.println(this.file + " 실행");
    }
}
 
 
cs

 

 

만들고보니 이것도 interface로 만들던가 혹은 최소한 abstract로 만드는 게 적절하지 않았나 싶다.

(요즘 대세는 어떻게든 꾸역꾸역 interface로 맞춰서 만드는 쪽인 것 같다.)

 

예제를 수정하기 귀찮으니 interface나 abstract라고 생각해주자.

이제 이어서 우리가 필요한 클래스를 정의해주면 된다.

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class BrushProgram extends Utility {
    public BrushProgram(String file) {
        super("BrushProgram");
    }
}
 
public class CaptureProgram extends Utility {
    public CaptureProgram(String file) {
        super("CaptureProgram");
    }
}
 
public class LeagueOfLegends extends Game{
    public LeagueOfLegends() {
        super("League of Legends");
    }
}
 
public class Starcraft extends Game {
    public Starcraft(String name) {
        super("Starcraft");
    }
}
 
cs

 

여기까지 만들었으면 클라이언트단에서 어떻게 사용할 수 있는지 직접 확인해보자.

 

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class example2_App {
    public static void main(String[] args) {
        BrushProgram2 brushProgram2 = new BrushProgram2();
        CaptureProgram2 captureProgram2 = new CaptureProgram2();
        LeagueOfLegends2 leagueOfLegends2 = new LeagueOfLegends2();
        Starcraft2 starcraft2 = new Starcraft2();
 
        brushProgram2.execute();
        captureProgram2.execute();
        leagueOfLegends2.execute();
        starcraft2.execute();
 
 
        File lol = new LeagueOfLegends();
        File star = new Starcraft();
        File brush = new BrushProgram("좋은 브러쉬 프로그램");
        File brush2 = new BrushProgram("구린 브러쉬 프로그램");
        lol.execute();
        star.execute();
        brush.execute();
        brush2.execute();
        
    }
}
cs

 

이전과 달리 File이라는 인터페이스 하나로 계층 내의 모든 객체들을 사용할 수 있게 되었다.

 

 

* 중요:  Leaf는 Component가 아닌 다른 객체에 의존성을 가지면 안된다.

(예 : 만약 Composite Pattern 적용 이전에는  LeagueOfLegends의 필드에서 StarCraft 타입을 참조하고 있었더라도 Composite Pattern으로 변경 시 LeagueOfLegends는 File 타입을 참조해야함)

 

Composite Pattern의 장점은 다음과 같다.

1. 객체들이 모두 같은 타입으로 취급되어 복잡한 트리 구조를 편리하게 사용할 수 있다.

2. 다형성과 재귀 알고리즘을 활용할 수 있다.

 

Composite Pattern의 단점은 다음과 같다.

: 공통 인터페이스에 묶어서 추상화하기 때문에 과하게 추상화할 경우 오히려 설계가 패턴에 종속되어 전체 흐름을 망칠 우려가 있다.

따라서 디자인 패턴 적용 시에는 늘 해당 패턴을 적용하는 목적을 잘 생각해야 한다.