본문 바로가기
개발자 준비/디자인패턴 & 아키텍쳐

반복자 패턴 & 합성 패턴

by osul_world 2021. 12. 30.
728x90

반복자 패턴


  • Iterator 패턴무언가 많이 모여있는 것을 하나씩 지정해서 순서대로 처리하는 패턴
  • 컬랙션이 새로 추가변경 되어도 모두 같은 방법으로 활용할수있어야한다. 별도의 순회자를 사용하여 ArrayList,List,Queue,복합타입 등 다양한 컬랙션 타입 데이들을 동일한 방법으로 사용할수있어야한다.

java.util에 패키지에 있는 Iterator 인터페이스에 대하여 먼저 알아야한다.

image

Java 8부터 remove라는 default 메서드가 추가되었다.

 

반복자 패턴의 적용

image

복합타입 자료구조의 내부 구현을 드러내지 않고 이 타입에 저장된 각 요소를 접근할 수 있도록 하기 위해

복합 타입이란 Class 처럼 int,String과 같은 기본타입들의 집합으로 이루어진 타입을 뜻한다.

다양한 순회 방법의 제공이 필요할 때

다양한 복합타입 자료구조에 대한 균일한 순회 인터페이스의 제공이 필요할 때

 

아래와 같은 복합타입 객체가 있다고 하자.

//Book : 한권의 책에 대한 정보를 가지고 있는 클래스
public class Book {
    private String name;

    public Book(String name) {
          this.name = name;
    }

    public String getName() {
        return name;
    }
}

 

이 책을 서재에 저장해두고 반복자 패턴을 이용해 저장된 책을 순차적으로 조회할것이다.

Generate 를 사용해 유연성을 올릴수있지만 이해를 위해 사용하지 않겠다.

//Iterable
public interface Iterable {
    public abstract Iterator getIterator();
}
//ConcereteIterable
public class BookShelf implements Iterable {
    private Book[] books; // 복합객체: 책의 집합
    private int last = 0; // 마지막 책이 꽂힌 위치

    public BookShelf(int size) {
        books = new Book[size];
    }

    public Book getBook(int index) {
        return books[index];
    }

    public int getLength() {
        return last;
    }

    // 책꽂이에 책을 꽂는다
    public void appendBook(Book book) {
        if (last < books.length) {
            this.books[last] = book;
            last++;
        } else {
            System.out.println("책꽂이가 꽉 찼습니다!");
        }
    }

    @Override
    public Iterator getIterator() {
        return new BookShelfIterator(this); //new ConcereteIterator(this)
    }
}
//ConcereteIterator  
//Iterator인터페이스는 java에서 제공
public class BookShelfIterator implements Iterator<Book> {
    private BookShelf bookShelf; // 검색을 수행할 책꽂이
    private int index = 0; // 현재 처리할 책의 위치

    public BookShelfIterator(BookShelf bookShelf) {
        this.bookShelf = bookShelf;
    }

    @Override
    public boolean hasNext() {
        return index < bookShelf.getLength();
    }

    @Override
    public Book next() {
        Book book = bookShelf.getBook(index);
        index++;
        return book;
    }
}
//Client
public class Main {

    public static void main(String[] args) {
        BookShelf bookShelf = new BookShelf(10);

        Book book1 = new Book("Bilbe");
        Book book2 = new Book("Cinderella");
        Book book3 = new Book("Daddy-Long-Legs");

        bookShelf.appendBook(book1);
        bookShelf.appendBook(book2);
        bookShelf.appendBook(book3);

        System.out.println("현재 꽂혀있는 책 : " + bookShelf.getLength() + "권");

        Iterator it = bookShelf.getIterator();
        
        while (it.hasNext()) { //외부 반복자
            Book book = (Book) it.next();
            System.out.println(book.getName());
        }
    }
}

이 코드를 보면 패턴의 구조는 쉽게 이해가 될것이다.

 

내부 반복자 vs. 외부 반복자

클라이언트가 반복을 제어하면 외부 반복자라 하고, 반복자가 반복을 제어하면 내부 반복자라 함

위 예제의 경우 외부 반복자이다. 대부분 외부 반복자를 사용한다.

 

패턴의 장단점

장점

• 클라이언트는 내부의 복잡한 구조를 이해하지 않아도 유지된 요소를 접근할 수 있음

• 구체적 복합타입은 내부 구조를 공개하지 않고 사용할 수 있도록 해줄 수 있음

• 다양한 종류의 순회 방법을 제공할 수 있음. 예) 이진 검색 트리: 전위, 중위, 후위

• 현재 최신 고급 프로그래밍 언어는 반복자를 통해 foreach for문을 제공하고 있음

• 복합타입에서 반복자를 위한 요소를 제거함으로써 복합타입 클래스의 응집성을 높임 (SRP). 직접 접근를 제한함으로써 강건성을 높일 수 있음

• 복합자료 전체를 한 번에 제공하는 것이 아니라 하나씩 제공함 (반복자의 구현방법 따라 차이가 있음)

• 반복 중간에 언제든지 중지한 후에 나중에 원할 때 해당 위치부터 다시 시작할 수 있음 (그 사이에 복합자료에 변할 수 있음)

단점

• 순서가 없는 복합자료(예: 집합)에 대해 어떤 순서가 있다고 오해할 수 있음

• 단순한 복합타입만 사용할 경우 반복자를 만들어 얻어지는 효과가 별로 없을 수 있음

• 어떤 경우에는 반복자를 이용한 접근이 효율성을 떨어뜨릴 수 있음

 

Reference

합성패턴(Composite Pattern)


 

컴포지트 패턴을 통해 트리 구조를 구현할 수 있다

전체-부분 관계를 표현하는 것이다.

파일 구조를 생각하면 이해하기 쉽다.

image

  • Component : 모든 표현할 요소들의 추상타입

    구현되는 자식들을 가지고 관리하기 위한 메소드(addChild, removeChild..)을 추상메서드로 제공한다.

  • Leaf : Component를 상속하고 개별 객체를 위한 행위 구현 (파일의 개념)

관리하기 위한 메소드(addChild, removeChild..)를 빈메서드나 예외 처리를 통해 사용할수없게 한다.

  • Composite : Component를 상속하고 자식들을 가지고 관리하기 위한 메소드(addChild, removeChild..)를 재정의한다. (폴더의 개념)

image

Composite는 폴더라고 생각하고 Leaf는 파일이라고 생각하면된다.

//Component 
public abstract class Component implements Iterable<Component>{
    public static String indent = "";
    private String name;
    private boolean hasChanged = false;
    
    public Component(String name){
    	this.name = name;
    }
    
    public abstract String list();
    public abstract void add(Node node); //추가
    public abstract void remove(Node node); //삭제
    public abstract Component getChild(int index); //조회
    public abstract Iterator<Component> iterator();
    public abstract Iterator<Component> childIterator();
}
//Leaf:  개별 객체를 위한 행위 구현
public class Leaf extends Component {

    public Leaf(String name){
	super(name); 
	}
    
    //관리를 위한 메서드들은 예외처리하여 사용할수없도록 함
	@Override 
    public void add(Node node) {
	throw new UnsupportedOperationException("단말노드");
	} 
	
    …
}
//Composite 
public class Composite  extends Component {
    private List<Component> childs = new ArrayList<>(); 
    
    public Composite(String name){
    	super(Component); 
    }
    
    //관리를 위한 메서드 재정의
    @Override 
    public void add(Component component) {
    	childs.add(component);
    }
    …
}

예제에서 볼수있듯이 컴포지트 패턴으로 트리 구조를 만들고 반복자 패턴으로 트리순회를 만들수있다.

합성 패턴은 모든 개별 객체를 접근하기 위한 반복자를 보통 함께 제공함

 

장점

• 현재 처리해야 하는 것이 중간 노드인지 단말 노드인지 구분하지 않고 처리할 수 있게 해줌

• 새로운 종류의 단말을 도입하기 쉬움

단점

• 단말과 중간 노드를 동일하게 취급하기 위해 추상 노드에 너무 많은 메소드가 선언 또는 정의될 수 있음. 이것은 ISP 원칙에 위배되는 사항이고 LSP 측면에서도 바람직하다고 보기 힘듦

 

728x90

'개발자 준비 > 디자인패턴 & 아키텍쳐' 카테고리의 다른 글

프록시 패턴(Proxy Pattern)  (0) 2021.12.30
스테이트 패턴 (State Pattern)  (0) 2021.12.30
템플릿 메소드 패턴  (0) 2021.12.29
어뎁터 패턴 & 파사드패턴  (0) 2021.12.29
명령패턴  (0) 2021.12.29