본문 바로가기
Design Pattern/구조 패턴(Structural patterns)

Composite Pattern, 복합체 패턴

by codeyaki 2024. 2. 5.
반응형

복합체 패턴이란?

컴포지트 패턴 혹은 복합체 패턴이라고 불리는 이 패턴은 객체들을 트리구조로 구성하여 개별객체와 복합 객체를 동일하게 취급할 수 있도록 하는 구조적 디자인패턴이다.

이를 통해서 단일객체와 복합객체를 일관된 방식으로 다룰 수 있게 된다. 

 

왜 사용해야 하는가?

  • 단일 객체와 복합 객체를 동일하게 취급하기 때문에 클라이언트 쪽에서 코드를 일관된 방식으로 사용할 수 있게 된다.
  • 재귀적인 구조로 편리하게 구현할 수 있다.
  • 새로운 단일 객체나 복합 객체를 추가할 때 기존의 코드를 수정하지 않는다.

어떤 경우에 사용해야 하는가?

  • 나무처럼 계층적인 부분-전체 구조를 갖고 있을 때 사용하면 좋다. 
    예를 들어, 그래픽 요소나, 문서 구조등이 있다.
  • 클라이언트가 일관된 인터페이스로 사용해야 할 때 사용하면 좋다.
데코레이터 패턴 모두 객체를 감싸는 데 사용한다.
하지만 데코레이터 패턴은 기능을 추가하거나 수정하는데 중점을 두지만, 
복합체는 부분과 전체를 일관된 방식으로 처리하는 데 중점을 둔다.

 

문제점

  • 재귀를 사용하기 때문에 객체가 많이 중첩된 경우 성능이 떨어질 수 있다.
  • 단일 객체와 복합객체를 동일한 인터페이스로 추상화해야 하기 때문에 설계가 어려워질 수 있다.

구현 방법

간단한 파일 시스템을 만들어 보자

여러 종류가 있지만 이중 Ordinary Files와 Directories를 가지고 와 간단하게 구현해 보자!

 

 

해당 구조를 토대로 직접 만들어보도록 한다.

먼저 File 인터페이스를 만들어서 이름과 사이즈를 구하는 메서드로 추상화를 진행하였다.


  
package com.example.designpattern.composite.file;
public interface File {
int getSize();
String getName();
}

해당 인터페이스들을 implements 일반 파일과 폴더를 구현해 주자!

 

먼저 다른 File을 담을 수 있는 복합객체인 Directory를 만들어주자.


  
package com.example.designpattern.composite.file;
import java.util.ArrayList;
import java.util.List;
public class Directory implements File{
private List<File> files;
private String name;
public Directory(String name) {
this.name = name;
files = new ArrayList<>();
}
public void addFile(File file) {
this.files.add(file);
}
public List<File> getFiles() {
return this.files;
}
@Override
public int getSize() {
int sumSize = 0;
for (File file : files) {
sumSize += file.getSize();
}
return sumSize;
}
@Override
public String getName() {
return this.name;
}
}

 

중요한 점은 getSize에 재귀적 특징을 이용해서 list에 들어있는 모든 하위 객체들의 size를 더해주는 것이다.

 

노드의 leaf에 해당하는 개별 객체인 Ordinary File 또한 구현해 주자.


  
package com.example.designpattern.composite.file;
public class OrdinaryFile implements File {
private String name;
private int size;
public OrdinaryFile(String name, int size) {
this.name = name;
this.size = size;
}
@Override
public int getSize() {
return this.size;
}
@Override
public String getName() {
return this.name;
}
}

 

특별할 것 없는 일반적인 객체로 계층의 leaf node에 해당한다.


  
package com.example.designpattern.composite;
import com.example.designpattern.composite.file.Directory;
import com.example.designpattern.composite.file.File;
import com.example.designpattern.composite.file.OrdinaryFile;
public class Main {
public static void main(String[] args) {
// 메인 복합객체 생성하기
Directory rootDirectory = new Directory("루트디렉터리");
// 메인복합객체에 단일객체 넣기
OrdinaryFile internet = new OrdinaryFile("인터넷", 50);
OrdinaryFile notePad = new OrdinaryFile("메모장", 5);
rootDirectory.addFile(internet);
rootDirectory.addFile(notePad);
// 복합객체 안에 복합객체 넣기
Directory gameDirectory = new Directory("게임디렉터리");
OrdinaryFile lostArk = new OrdinaryFile("잃어버린 방주", 2550);
OrdinaryFile lol = new OrdinaryFile("전설들의 경기", 1200);
gameDirectory.addFile(lostArk);
gameDirectory.addFile(lol);
rootDirectory.addFile(gameDirectory);
// 정보 출력하기
// 복합객체 확인
printSize(rootDirectory);
printSize(gameDirectory);
// 단일 객체 확인
printSize(lostArk);
}
private static void printSize(File file) {
int sumSize = file.getSize();
System.out.println(file.getName() + "의 용량은 " + sumSize + "입니다.");
}
}

 

해당 클래스를 사용하는 클라이언트의 입장에선 복합객체나 개별객체들을 동일하게 사용할 수 있는 모습을 확인할 수 있다.


  
루트디렉터리의 용량은 3805입니다.
게임디렉터리의 용량은 3750입니다.
잃어버린 방주의 용량은 2550입니다.
반응형