CS/디자인패턴

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

mabb 2023. 6. 19. 11:27
반응형

▶디자인 패턴 ( Design Pattern)

:프로그램 개발 시 문제 해결을 위하여 빈번히 사용되는 개발자들의 경험, 내적인 축적에 대하여,
GoF(Gang of Four) 라 불리는 4인의 개발자들이 각각을 패턴으로 정의하고 이름을 붙였다.
이를 디자인 패턴 (Design Pattern) 이라고 한다.
 23개의 디자인 패턴을
『Elements of Reusable Object-Oriented Software』
라는 책으로 발간하였다.

Gang of Four 분들은 모두 안경을 썼다.

 

▷디자인 패턴의 용어를 빌리면 서로의 아이디어를 보다 용이하게 비교, 논의할 수 있게 된다.
▷재사용과 기능확장이 쉬운 소프트웨어를 만들기 위한 유익한 기법이 바로 디자인 패턴이다.

 

---------------------------------------------------------
1. Iterator
2. Adapter
3. Template Method
4. Factory Method
5. Singleton
6. Prototype
7. Builder
8. Abstract Factory
9. Bridge
10. Strategy
11. Composite
12. Decorator
13. Visitor
14. Chain of Responsibility
15. Facade
16. Mediator
17. Observer
18. Memento
19. State
20. Flyweight
21. Proxy
22. Command
23. Interpreter
---------------------------------------------------------

▶Composite 패턴

"그릇과 내용물을 동일시"
"트리 구조에서의 재귀적 활용"


-왜 사용하는가
-클래스다이어그램
-각 클래스(인터페이스)의 역할
-Java소스

▶Composite 패턴에 대한 이해

-그릇과 내용물을 동일시해서 재귀적인 구조를 만들기 위한 디자인 패턴이다.

-내용물과 내용물을 담고 있는 복합체를 동일시한다.

-여러 개를 모아서 마치 하나인 것처럼 취급할 수 있다.(재귀적)

-복수와 단수를 동일시한다.

-내용물과 복합체를 동일시하는 역할을 컴포넌트역할이라고 칭한다.

-컴포넌트는 내용물과 복합체의 공통적인 작업을 정한다.

-내용물을 Leaf라고 칭한다.

-재귀적 구조로 내용물 계층을 타고 들어간다.

-예를 들면 파일과 디렉터리를 동일시하여 다루는 것이다.

-내용물과 복합체를 동일시하지만 복합체에서만 사용가능한 메서드가 있을 수 있고 내용물과 복합체에서 다르게 동작하여야 하는 메소드가 있다.

-특정 하위 클래스에서만 사용할 수 있는 메서드를 구현하는 방법으로는 컴포넌트(상위클래스)에서 해당 메소드를 예외처리하고 메소드를 사용할 클래스에서 의미 있는 구현으로 Override 하는 방법이 있다.

-컴포넌트(상위 클래스)에서 해당 메서드에 아무것도 구현하지 않는 방법도 있다. 이 경우 해당 메서드를 사용하기 위해 구현한 자식 클래스가 아닌 경우 메소드를 실행하여도 아무 일도 일어나지 않는다.

-트리 구조의 데이터 구조는 일반적으로 Composite패턴으로 다룬다.

-자바에서는 this와 문자열을 더하면 자동적으로 해당 오브젝트의 toString()으 호출한다.

 

▶왜 사용하는가

트리 구조에서 사용한다. 복합체와 내용물을 동일시하여 재귀적 구조로 처리할 때 사용한다.

▶클래스다이어그램

Composite패턴의 클래스다이어그램

 

▶각 클래스(인터페이스)의 역할

▷Component 역할
복합체와 내용물을 동일시하는 역할을 한다. 복합체와 내용물의 공통적인 메서드를 정의한다.

▷Composite 역할
복합체 역할.

▷Leaf 역할
내용물 역할( 하위 노드가 없다)

▷Exception 역할
복합체와 내용물이 다르게 동작하도록 예외처리하기 위한 역할

▷Main 역할
노드를 추가하여 트리구조로 만들고 복합체와 내용물을 동일시하여 작업을 요청한다.

 

▶Java 소스

*java1.8 이전 버전을 사용하는 교재를 참고하였습니다.
Java API 문서 등을 참조하여 제네릭을 적용하는 등 소스를 개선할 필요가 있습니다.
(https://docs.oracle.com/en/java/javase/20/docs/api/)

▷Entry.java

package main.java.designpattern.composite;

public abstract class Entry {
    public abstract String getName();
    public abstract int getSize();
    public Entry add(Entry entry) throws FileTreatmentException{
        throw new FileTreatmentException();
    }

    public void printList(){
        printList(" ");
    }
    protected abstract void printList(String prefix);
    @Override
    public String toString(){
        return getName() + " (" + getSize() + ") ";
    }
}

▷File.java

package main.java.designpattern.composite;

public class File extends Entry{
    private String name;
    private int size;

    public File(String name, int size){
        this.name = name;
        this.size = size;
    }
    @Override
    public String getName() {
        return name;
    }
    @Override
    public int getSize() {
        return size;
    }
    @Override
    protected void printList(String prefix) {
        System.out.println(prefix + "/" + this);
    }
}

▷Directory.java

package main.java.designpattern.composite;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Directory extends Entry{
    private String name;
    private List<Entry> directory = new ArrayList<>();

    public Directory(String name){
        this.name = name;
    }
    @Override
    public String getName() {
        return name;
    }
    @Override
    public int getSize() {
        int size = 0;
        Iterator<Entry> it = directory.iterator();
        while(it.hasNext()){
            Entry entry = it.next();
            size += entry.getSize();
        }
        return size;
    }
    @Override
    protected void printList(String prefix) {
        System.out.println(prefix + "/" + this);
        Iterator<Entry> it = directory.iterator();
        while(it.hasNext()){
            Entry entry = it.next();
            entry.printList(prefix + "/" + name);
        }
    }
    @Override
    public Entry add(Entry entry){
        directory.add(entry);
        return this;
    }
}

▷Main.java

package main.java.designpattern.composite;

public class Main {
    public static void main(String[] args){
        try {

            System.out.println("루트 엔트리 생성중...");
            Directory rootDir = new Directory("root");
            Directory binDir = new Directory("bin");
            Directory tmpDir = new Directory("tmp");
            Directory usrDir = new Directory("usr");
            rootDir.add(binDir);
            rootDir.add(tmpDir);
            rootDir.add(usrDir);
            binDir.add(new File("vi", 10000));
            binDir.add(new File("latex", 20000));
            rootDir.printList();

            System.out.println("");
            System.out.println("유저 엔트리 생성중...");
            Directory kim = new Directory("kim");
            Directory park = new Directory("park");
            Directory son = new Directory("son");
            usrDir.add(kim);
            usrDir.add(park);
            usrDir.add(son);
            kim.add(new File("diary.html", 100));
            kim.add(new File("Java.java", 200));
            park.add(new File("memo.txt", 300));
            son.add(new File("son.txt", 500));
            son.add(new File("TodoList.txt", 300));
            rootDir.printList();

        }catch(FileTreatmentException e){
            e.printStackTrace();
        }
    }
}

▷FileTreatmentException.java

package main.java.designpattern.composite;

public class FileTreatmentException extends RuntimeException {
    public FileTreatmentException(){}
    public FileTreatmentException(String msg){
        super(msg);
    }
}

---------------------------------------------------------
※참고자료
-Java언어로 배우는 디자인 패턴 입문
-Headfirst Design Pattern
-나무위키, 위키백과, 네이버백과
-리팩토링구루(https://refactoring.guru/ko)

반응형