
컴포지트(Composite)
컴포지트 디자인 패턴은 객체들을 트리 구조로 구성하여 부분-전체 계층을 표현하는 디자인 패턴이다. 이 패턴은 사용자가 개별 객체와 객체의 조합을 동일하게 다룰 수 있도록 해준다. 컴포지트 패턴의 명칭은 영어 단어 'Composite'에서 유래했으며, 이는 '여러 요소를 조합하여 전체를 만드는 것'을 의미한다.
컴포지트 패턴 미적용 예시
먼저, 컴포지트 패턴을 적용하지 않았을 때의 문제를 드러내는 예시 코드를 살펴보자. 파일과 폴더를 다루는 시스템을 만든다고 했을 때, 각각을 다루는 클래스를 별도로 구현해야 한다.
// 파일 클래스
class File {
public string Name { get; set; }
public void Display() => Console.WriteLine(Name);
}
// 폴더 클래스
class Folder {
public string Name { get; set; }
private List<File> files = new List<File>();
public void AddFile(File file) {
files.Add(file);
}
public void Display() {
Console.WriteLine(Name);
foreach(var file in files) {
file.Display();
}
}
}
class Program {
static void Main(string[] args) {
File file1 = new File { Name = "File1" };
File file2 = new File { Name = "File2" };
Folder folder = new Folder { Name = "Folder" };
folder.AddFile(file1);
folder.AddFile(file2);
folder.Display();
}
}
이 예시에서 문제점은 폴더 내에 또 다른 폴더를 추가하는 상황을 처리할 수 없다는 것이다. 즉, 파일과 폴더를 일관된 방식으로 다루기 어렵다.
컴포지트 패턴 적용 예시
컴포지트 패턴을 적용하면, 파일과 폴더를 동일한 인터페이스로 다룰 수 있게 되어, 구조의 유연성이 크게 향상된다.
// 컴포넌트 인터페이스
interface IComponent {
void Display();
}
// 파일 클래스
class File : IComponent {
public string Name { get; set; }
public void Display() => Console.WriteLine(Name);
}
// 폴더 클래스
class Folder : IComponent {
public string Name { get; set; }
private List<IComponent> children = new List<IComponent>();
public void AddChild(IComponent component) {
children.Add(component);
}
public void Display() {
Console.WriteLine(Name);
foreach(var child in children) {
child.Display();
}
}
}
class Program {
static void Main(string[] args) {
File file1 = new File { Name = "File1" };
File file2 = new File { Name = "File2" };
Folder folder1 = new Folder { Name = "Folder1" };
Folder folder2 = new Folder { Name = "Folder2" };
folder1.AddChild(file1);
folder2.AddChild(file2);
folder2.AddChild(folder1); // 폴더 내에 폴더를 추가할 수 있다.
folder2.Display();
}
}
컴포지트 패턴을 적용한 이 코드는 폴더 내에 다른 폴더를 추가하는 것을 포함하여, 파일과 폴더를 동일하게 다루는 것을 가능하게 한다. 이로 인해 트리 구조를 더 자연스럽게 표현할 수 있으며, 코드의 유연성과 재사용성이 향상된다.
폴더 추가 방법
컴포지트 패턴에서의 핵심 개념은 Component 인터페이스(또는 추상 클래스)를 통해 개별 객체(Leaf)와 그것들의 조합(Composite)을 동일한 인터페이스로 다룰 수 있게 하는 것이다. 이 패턴을 통해 클라이언트는 개별 객체와 객체의 조합을 구분하지 않고 일관된 방식으로 다룰 수 있다.
문법적 설명
- Component 인터페이스: 이 인터페이스는 Leaf 와 Composite 모두가 구현해야 하는 공통의 인터페이스다. 예제에서는 IComponent 가 이 역할을 하며, Display 메소드를 가지고 있다. 이 메소드는 컴포지트 내의 모든 요소가 공통적으로 수행해야 하는 동작을 정의한다.
- Leaf 클래스: 실제 객체를 나타내며, 여기서는 File 클래스가 Leaf 역할을 한다. Leaf 는 Component 인터페이스의 메소드를 구현한다. 여기서는 Display 메소드를 통해 자신의 이름을 출력한다.
- Composite 클래스: Leaf 객체들의 조합을 나타내며, 내부적으로 Component 인터페이스를 구현한 객체들의 집합을 관리한다. 예제에서 Folder 클래스가 이 역할을 한다. Folder는 내부적으로 IComponent 타입의 리스트(children)를 가지고 있으며, 이를 통해 파일 또는 다른 폴더를 자식으로 추가할 수 있다(AddChild 메소드).
상세 풀이
컴포지트 패턴을 적용하지 않은 경우, 폴더 내에 다른 폴더를 추가하는 것은 자연스럽지 않다. Folder 클래스가 오직 File 객체들만을 자식으로 가질 수 있기 때문이다. 따라서, 폴더와 파일을 다루는 방식에 일관성이 없고, 확장성이 떨어진다.
컴포지트 패턴을 적용하면, Folder와 File 모두가 IComponent 인터페이스를 구현하므로, Folder는 File 뿐만 아니라 다른 Folder 객체들도 자식으로 추가할 수 있다. 이는 Folder 클래스 내의 children 리스트가 IComponent 타입의 객체들을 저장하기 때문이다. 이러한 방식으로, 폴더 내에 또 다른 폴더를 추가하는 것이 가능해진다. 이를 통해 파일 시스템의 트리 구조와 같은 복잡한 계층적 구조를 효과적으로 모델링할 수 있다.
결론적으로, 컴포지트 패턴은 객체의 조합을 마치 단일 객체처럼 다룰 수 있게 해주어, 클라이언트 코드가 복잡해지는 것을 방지하고, 코드의 재사용성과 확장성을 향상시킨다.
'컴퓨터 과학 > 디자인패턴' 카테고리의 다른 글
[디자인패턴] 구조 패턴(4) : 플라이웨이트(Flyweight) (0) | 2024.03.25 |
---|---|
[디자인패턴] 구조 패턴(3) : 프록시(Proxy) (2) | 2024.03.24 |
[디자인패턴] 구조 패턴(1) : 어댑터(Adapter) (1) | 2024.03.24 |
[디자인패턴] 생성 패턴(6) : 싱글톤(Singleton) (0) | 2024.03.24 |
[디자인패턴] 생성 패턴(5) : 프로토타입(Prototype) (0) | 2024.03.24 |