JAVA 언어로 배우는 디자인 패턴 입문, 헤드퍼스트 디자인 패턴을 통해 학습 중이며 두 책의 예제 위주로 정리.
생성패턴(Creational Pattern)
1. 싱글톤(Singleton)
2. 빌더(Builder)
3. 팩토리 메소드(Factory Method)
4. 추상 팩토리(Abstract Factory)
5. 프로토타입(Prototype)
구조패턴(Structural Pattern)
2. 브릿지(Bridge)
3. 컴포지트(Composite)
4. 데코레이터(Decorator)
5. 퍼사드(Facade)
6. 플라이웨이트(flyweight)
7. 프록시(Proxy)
행동(행위) 패턴(Behavioral Pattern)
1. 책임 연쇄(Chain of Responsibility)
2. 커맨드(Command)
3. 인터프리터(Interpreter)
4. 반복자(Iterator)
5. 중재자(Mediator)
6. 메멘토(Memento)
7. 옵저버(Observer)
8. 상태(State)
11. 방문자(Visitor)
반복자(Iterator)는 행위패턴(Behavioral Pattern)에 속하는 디자인 패턴이다.
자바에서 배열의 모든 요소를 표현하기 위해서는 반복문을 사용해 처리하는 경우가 많다.
for문을 통해 사용하게 되는 경우가 많고 이 경우 i 라는 인덱스 변수를 사용해 i를 증가시켜가며 검색하게 되는데
이 인덱스 변수인 i 의 기능을 추상화 하여 일반화 한 것을 디자인 패턴에서는 Iterator 패턴이라고 한다.
무엇인가 많이 모여있을 때 이를 순서대로 가리키며 전체를 검색하고 처리를 반복하는 것이다.
Iterate의 의미는 '반복하다'라는 의미이기 때문에 Iterator를 반복자라고 부른다.
예제
예제 프로그램으로는 책장(BookShelf) 안에 책(Book)을 넣고 책 이름을 차례대로 표시하는 프로그램이다.
//예제 출처 - JAVA 언어로 배우는 디자인 패턴 입문
public class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
import java.util.Iterator;
public BookShelf implements Iterable<Book> {
private Book[] books;
private int last = 0;
public BookShelf(int maxSize) {
this.books = new Book[maxSize];
}
public Book getBookAt(int index) {
return books[index];
}
public void appendBook(Book book) {
this.books[last] = book;
last++;
}
public int getLength() {
return last;
}
@Override
public Iterator<Book> iterator() {
return new BookShelfIterator(this);
}
}
import java.util.Iterator;
import java.util.NoSuchElementException;
public class BookShelfIterator implements Iterator<Book> {
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
@Override
public boolean hasNext() {
if(index < bookShelf.getLength())
return true;
else
return false;
}
@Override
public Book next() {
if(!hasNext())
throw new NoSuchElementException();
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
import java.util.Iterator
public class BookMain {
public static void main(String[] args) {
BookShelf bookShelf = new BookShelf(4);
bookShelf.appendBook(new Book("Around the world in 80 Days"));
bookShelf.appendBook(new Book("Bible"));
bookShelf.appendBook(new Book("Cinderella"));
bookShelf.appendBook(new Book("Daddy-Long-Legs"));
//명시적으로 Iterator를 사용하는 방법
//Iterator 인스턴스를 생성
Iterator<Book> it = bookShelf.iterator();
//hasNext()가 false가 될때까지.
//즉, 다음 객체가 존재하지 않을때까지
//book에 next()로 값을 받아오고 책 이름을 출력한다.
while(it.hasNext()) {
Book book = it.next();
System.out.println(book.getName());
}
System.out.println();
//확장 for문을 사용하는 경우
for(Book book : bookShelf)
System.out.println(book.getName());
}
}
구조
BookShelf
- 책장을 나타내는 클래스. Iterable<book> 인터페이스를 구현한다.
- books라는 Book 타입 배열이 존재하며, 크기는 BookShelf 인스턴스 생성 시 생성자 인수를 통해 결정된다.
- last는 appendBook() 메소드를 통해 books에 데이터가 추가될때마다 하나씩 증가하게 되며 배열의 인덱스를 담당한다.
- 그 외 메소드로는 배열의 가장 끝에 있는 데이터를 반환하는 getBookAt()과 인덱스를 반환하는 getLength()가 있다.
- 마지막으로 Iterable<Book> 인터페이스를 구현한 iterator() 메소드가 존재한다.
- 이 메소드는 BookShelf 클래스에 대응하는 Iterator로서, BookShelfIterator 클래스의 인스턴스를 생성하여 반환한다.
- 책장에 꽂혀있는 책을 반복해서 처리하고자 할 때 이 iterator() 메소드를 호출.
BookShelfIterator
- BookShelf 클래스의 검색을 실행하는 클래스이다.
- 생성자에서는 BookShelf 인스턴스를 인수로 받아 bookShelf 필드에 담아주고 index 필드는 0으로 초기화 한다.
- Iterator<Book> 인터페이스를 구현한다.
- hasNext() 메소든느 다음 책이 있는지 확인하고 다음 책이 있다면 true, 없다면 false를 리턴하도록 한다.
- next() 메소드는 다음 책이 존재하지 않는다면 NoSuchElementException을 발생시키고 존재한다면 해당 객체를 반환한다.
- 반환 전 index 필드 값을 하나 미리 증가시켜 다음 next() 를 준비하도록 한다.
Iterable<E>
- 처리를 반복할 대상을 나타내는 것으로 java.lang에 선언되어 있다.
- 이 인터페이스의 구현체는 배열같은 집합체가 된다. 예제에서는 Book을 모은 인터페이스를 사용하기 때문에 Iterable<Book>으로 선언한다.
- Iterable 인터페이스에는 iterator 메소드가 선언되어있는데 집합체에 대응하는 Iterator<E>를 만들도록 하기 위함이다.
- 집합체에 포함된 요소를 하나하나 처리해나가고자 할 때는 iterator 메소드를 사용해 Iterator<E> 인터페이스를 구현한 클래스의 인스턴스를 하나 만든다.
Iterator<E>
- 하나하나의 요소 처리를 반복하기 위한 것으로 루프 변수(i) 와 같은 역할을 한다.
- 선언되는 메소드로는 hasNext()와 next()가 있다.
- hasNext()는 다음 요소가 존재하는지 여부에 대한 것이고 next()는 다음 요소를 가져오는 것이다.
- next()의 경우는 보이지 않는 곳에서 다음에 다시 호출되었을 때 다음 요소를 반환할 수 있도록 사전에 미리 준비를 하게 된다.
예제 프로그램에서의 역할들
Iterator(반복자) 역 - Iterator<Book>
- 요소를 순서대로 검색하는 인터페이스를 결정한다.
- 다음 요소의 존재여부를 파악하는 hasNext()와 다음 요소를 가져오는 next()를 결정한다.
ConcreteIterator(구체적인 반복자) 역 - BookShelfIterator
- Iterator 인터페이스를 구현하는 구현체 역할이다.
- 검색에 필요한 정보를 가지고 잇어야 하기 때문에 BookShelf 클래스의 인스턴스를 bookShelfIterator 클래스의 bookShelf 필드에서 기억하고, 검색중인 위치를 index 필드에서 기억한다.
Aggregate(집합체) 역 - Iterable<Book>
- Iterator를 만들어내는 인터페이스를 결정한다.
- 이 인터페이스는 내가 가진 요소를 차례대로 검색해 주는 것을 만들어내는 메소드이다.
ConcreteAggregate(구체적인 집합체) 역 - BookShelf
- Aggregate가 결정한 Iterable 인터페이스를 실제로 구현하는 구현체 역할이다.
- 구체적인 Iterator 역할인 ConcreateIterator의 인스턴스를 만들어 낸다.
for 문 대신 Iterator를 사용하는 이유
Iterator도 for문처럼 반복을 통해 다음 값을 가져오게 되는데 굳이 Iterator를 써야 하는 이유가 무엇일까.
이유는 구현과 분리해서 사용할 수 있기 때문이다.
Iterator의 hasNext()와 next()는 Iterator의 메소드일 뿐이지 실질적인 BookShelf의 구현에 사용되는 것은 아니다.
또한 Iterator 인터페이스는 BookShelf의 메소드에 접근해야할 이유도 없다.
기존 예제는 배열을 사용하고 있지만 만약 리스트로 수정하고자 한다면?
Iterator를 사용하는 경우에는 BookShelf에서 Book[] 을 List<Book>으로 수정하고 appendBook에서 this.books.add로 수정만 해주면 된다.
import java.util.Iterator;
public BookShelf implements Iterable<Book> {
//private Book[] books;
private List<Book> books;
private int last = 0;
public BookShelf(int maxSize) {
this.books = new Book[maxSize];
}
public Book getBookAt(int index) {
return books[index];
}
public void appendBook(Book book) {
//this.books[last] = book;
this.books.add(book);
last++;
}
public int getLength() {
return last;
}
@Override
public Iterator<Book> iterator() {
return new BookShelfIterator(this);
}
}
이렇게 BookShelf 하나만 수정하면 메인클래스 포함 다른 클래스에서는 수정이 필요없어진다.
반면, Iterator를 사용하지 않는다면 메인 메소드에서는 books[i].getName() 형태로 출력하게 되었을 것이고,
그럼 이렇게 처리되고 있는 모든 것들에 대해 books.get(i) 형태로 수정해야 했을 것이다.
Iterator를 사용하게 되면서 코드 재사용성이 높아졌다고 볼 수 있다.
또한, 자바에서는 사용하지 않는 인스턴스에 대해 GarbegeCollection을 통해 자동으로 삭제되기 때문에 iterate에 대응하는 deleteIterator 메소드는 불필요하다.
그리고 메인메소드에서 추가로 하단에 확장 for문을 사용한 것을 볼 수 있다.
확장 for문의 구조를 보면 for(Book book : bookShelf) 로 처리하여 간단하게 Iterator를 통한 처리와 마찬가지로 book.getName()으로 출력할 수 있는것을 볼 수 있다.
이 코드로 알 수 있는 점으로는 확장 for문은 Iterator와 동일한 처리를 하고 있는 것을 알 수 있다.
물론 bookShelf에 Book 배열이 들어가있는 것이기 때문에 동일하게 Iterator가 구현이 되어있어야 이렇게 사용할 수 있게 된다.
단지 hasNext와 next를 굳이 명시하지 않아도 된다는 편의성이 제공되는 것이다.
'JAVA > Design Pattern' 카테고리의 다른 글
디자인패턴(Java) - 템플릿 메소드(Template Method) (0) | 2023.12.27 |
---|---|
디자인패턴(Java) - 전략패턴(Strategy Pattern) (1) | 2023.12.19 |
디자인패턴(Java) - 어댑터(Adapter) (0) | 2023.12.16 |
디자인패턴(Design Pattern)이란 (0) | 2023.12.15 |