Composite 패턴

5.디자인패턴 2016. 9. 17. 20:17

Composite 패턴은 "복합 객체(Composite Object)"와 "단일 객체(Leaf Object)"를 동일하게 취급하고 싶을 때 사용하는 패턴이다.

일반적으로 가장 이해하기 쉬운 비유는 디렉토리와 파일의 관계이다. 디렉토리는 복합 객체와 유사하고, 파일은 단일 객체와 유사한 특성을 가지고 있다. 디렉토리는 자기 내부에 같은 디렉토리 또는 파일을 가지고 있을 수 있다. 파일은 단일 객체이기 때문에 내부에 다른 객체를 가지고 있을 수 없다. 만약 디렉토리 - 파일 관계를 보다 단순화 시키고자 한다면 디렉토리와 파일을 동일한 타입으로 취급하는 방법을 사용할 수 있을 것이다.

물론 모든 동작들을 이와 같이 만들 수는 없다. 적어도 디렉토리와 파일이 유사한 동작을 할 수 있는 경우에만 가능하다. 아래의 예제에서는 파일의 갯수를 세는 동작과 파일의 라인 수(공백을 제외한)를 세는 동작에 대해서 정의하고 있다. 디렉토리는 파일이 아니고, 내부에 파일들을 가지고 있기 때문에 파일 개수를 세는 동작을 단일 객체인 파일에 위임할 수 있다. 또한 라인 수를 계산하는 동작에 대해서도 디렉토리는 항상 파일에게 이를 위임한다. 파일은 파일 갯수에 대해 물어보면 항상 1이라고 대답한다. 라인 수에 대한 응답은 파일을 열어 공백을 제외한 라인의 수를 제공하도록 한다. 만약 디렉토리와 파일이 동일한 상위 타입을 가지고 있다면 외부에서는 최 상위 디렉토리에게 파일의 수와 라인 수를 물어보는 것만으로도 전체 디렉토리 구조 상에서의 정보들을 얻어 낼 수 있을 것이다. 이런 복잡한 동작을 단순하게 만들어 줄 수 있는 것이 Composite 패턴이다.


Composite 패턴 다이어그램

Item 인터페이스는 디렉토리와 파일로부터 파일 갯수(getFiles()) 및 라인 수(getLines())를 얻어 올 수 있는 공통 인터페이스를 정의한다. 그리고 디렉토리를 구현한 DirectionItem과 파일을 구현한 FileItem은 외부에서 동일하게 취급될 수 있도록 Item 인터페이스를 구현한다. FileItem은 단일 객체로써, 자신의 파일 수( = 항상 1)와 라인 수를 계산하여 넘겨주도록 구현된다. DirectoryItem은 복합 객체로서 내부에 하위 디렉토리 및 하위 파일들을 담을 수 있는 List 를 소유한다.

DirectoryTree 클래스는 실제 디렉토리를 방문하면서 디렉토리 및 파일의 트리 구조를 만들고 최종 결과를 얻어 낼 수 있는 인터페이스를 가지고 있다. 이를 위해서 DirectoryTree 객체는 makeTree() 메소드를 통해서 Item 객체들을 생성하고 디렉토리 구조를 만드는 작업을 수행한다.

아래는 위의 클래스 다이어그램을 구현한 코드이다.


Composite 패턴의 구현

interface Item{

    public int getFiles();

    public int getLines();

}

class DirectoryItem implements Item{

    List<Item> children = new ArrayList<Item>();

    public void addChild(Item child){ children.add(child); }

    File file;

    public DirectoryItem(File file){

        this.file = file;

    }

    public int getFiles(){

        int result = 0;

        for(Item each : children){

            result += each.getFiles();

        }

        return result;

    }

    public int getLines(){

        int result = 0;

        for(Item each : children){

            result += each.getLines();

        }

        return result;

    }

}

class FileItem implements Item{

    File file;

    public FileItem(File file){ this.file = file; }

    public int getFiles(){

        return 1;

    }

    public int getLines(){

        Scanner scan;

        int result = 0;

        try {

        scan = new Scanner(file);

        while(scan.hasNextLine()){

            String line = scan.nextLine();

            if(!line.equals("")) result ++;

        }

        } catch (FileNotFoundException e) {

            System.out.println("File Not Found");

        }

        return result;

    }

}


Composite 패턴을 이용하여 디렉토리 트리를 만드는 DirectoryTree 클래스

class DirectoryTree{

    String path;

    public DirectoryTree(String path){ this.path = path; }

    Item items;

    public void makeTree(){

        File file = new File(path);

        items = tourDirectory(file);

    }

    private Item tourDirectory(File file){

        DirectoryItem directory = new DirectoryItem(file);

        File[] files = file.listFiles();

        for(File each : files){

            if(each.isDirectory()){

                Item childDirectory = tourDirectory(each);

                directory.addChild(childDirectory);

            }

            else{

                FileItem childFile = new FileItem(each);

                directory.addChild(childFile);

            }

        }

        return directory;

    }

    public int getFiles(){

        return items.getFiles();

    }

    public int getLines(){

        return items.getLines();

    }

} 


사용 방법

public static void main(String[] args) {

    DirectoryTree treenew DirectoryTree("C:/JavaDesignPattern/src/GofPattern/");

    tree.makeTree();

    System.out.println(tree.getFiles());

    System.out.println(tree.getLines());

} 


여러 다른 타입의 객체들을 동일한 타입으로 취급할 수 있게 되면 구현은 단순해지기 마련이다. Composite 패턴에서는 복합 객체와 단일 객체가 동일하게 취급된다. 그리고 자칫 복잡해질 수도 있는 복합 객체가 하위 객체들에게 작업을 위임하는 방식으로 동작함으로써 구현이 단순해졌음을 알 수 있다. 

'5.디자인패턴' 카테고리의 다른 글

Flyweight 패턴  (0) 2016.09.18
Chain Of Responsibility 패턴  (0) 2016.09.17
Iterator 패턴  (0) 2016.09.17
Enum Abstract Factory 패턴  (0) 2016.09.16
Proxy 패턴과 그 활용  (0) 2016.09.16
Posted by 이세영2
,