반복자 패턴
- Iterator 패턴은 무언가 많이 모여있는 것을 하나씩 지정해서 순서대로 처리하는 패턴
- 컬랙션이 새로 추가변경 되어도 모두 같은 방법으로 활용할수있어야한다. 별도의 순회자를 사용하여 ArrayList,List,Queue,복합타입 등 다양한 컬랙션 타입 데이들을 동일한 방법으로 사용할수있어야한다.
java.util에 패키지에 있는 Iterator 인터페이스에 대하여 먼저 알아야한다.
Java 8부터 remove라는 default 메서드가 추가되었다.
반복자 패턴의 적용
복합타입 자료구조의 내부 구현을 드러내지 않고 이 타입에 저장된 각 요소를 접근할 수 있도록 하기 위해
복합 타입이란 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)
컴포지트 패턴을 통해 트리 구조를 구현할 수 있다
전체-부분 관계를 표현하는 것이다.
파일 구조를 생각하면 이해하기 쉽다.
Component : 모든 표현할 요소들의 추상타입
구현되는 자식들을 가지고 관리하기 위한 메소드(addChild, removeChild..)을 추상메서드로 제공한다.
Leaf : Component를 상속하고 개별 객체를 위한 행위 구현 (파일의 개념)
관리하기 위한 메소드(addChild, removeChild..)를 빈메서드나 예외 처리를 통해 사용할수없게 한다.
- Composite : Component를 상속하고 자식들을 가지고 관리하기 위한 메소드(addChild, removeChild..)를 재정의한다. (폴더의 개념)
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 측면에서도 바람직하다고 보기 힘듦
'개발자 준비 > 디자인패턴 & 아키텍쳐' 카테고리의 다른 글
프록시 패턴(Proxy Pattern) (0) | 2021.12.30 |
---|---|
스테이트 패턴 (State Pattern) (0) | 2021.12.30 |
템플릿 메소드 패턴 (0) | 2021.12.29 |
어뎁터 패턴 & 파사드패턴 (0) | 2021.12.29 |
명령패턴 (0) | 2021.12.29 |