티스토리 뷰

반응형

반복자 패턴(Iterator Pattern)

반복 작업을 Iterator 인터페이스로 캡슐화해 데이터가 저장될 컬렉션을 외부로 노출시키지 않으면서 컬렉션 내 모든 항목에 접근할 수 있는 패턴입니다.

 

  • 어떤 식으로 구현되어 있는지 전혀 모르는 상태에서 반복 작업을 수행할 수 있습니다.
  • 배열이든, 리스트이든 컬렉션의 종류와 상관없이 작업을 처리할 수 있습니다.
  • 모든 항목에 일일이 접근하는 작업을 컬렉션이 아닌 반복자 객체가 담당합니다.

 

 

구조

출처: https://refactoring.guru/ko/design-patterns/iterator

 

1. Iterator 인터페이스: 모든 반복자가 구현해야 하는 인터페이스로 컬렉션을 순회하는데 필요한 작업들을 제공합니다.

2. ConcreateIterator 구상 클래스: 컬렉션을 순회하기 위한 알고리즘을 구현합니다.

3. IterableCollection 인터페이스: 반복자로 관리할 컬렉션들의 공통 인터페이스를 정의합니다.

4. ConcreateCollection 구상 클래스: 클라이언트가 사용할 객체 컬렉션을 갖고 있으며, Iterator로 리턴하는 메소드를 구현해야 합니다.

5. Client: 인터페이스를 통해 반복자와 컬렉션을 사용합니다.

 

 

 

단일 책임 원칙(SRP)

객체 지향 설계 5원칙 중 하나로, 다음과 같은 의미를 가지고 있습니다.

어떤 클래스가 바뀌는 이유는 단 하나뿐이어야 한다.

 

어떤 클래스가 맡고 있는 모든 역할은 나중에 코드의 변화를 불러올 수 있습니다.

즉 2개 이상의 역할을 맡으면 바뀔 수 있는 부분이 2개 이상이 될 수 있습니다.

 

이렇듯 한 클래스가 특정 역할을 얼마나 일관되게 지원하는지를 측정하는 척도로 '응집도'를 사용합니다.

응집도가 높을 수록 서로 연관된 기능이 묶여있음을 의미합니다.

 

 

반복자 패턴은 컬렉션으로부터 Iterator 클래스로 반복자용 메소드를 분리하기 때문에 SRP 원칙을 잘 준수하고 있는 예시로 볼 수 있습니다.

 

 

 

예시

식당과 팬케이크 가게에서 사용하는 서로 다른 메뉴판을 하나의 메뉴판으로 합쳐봅시다!

 

팬케이크 가게의 메뉴들

public class PancakeHouseMenu {
    List<MenuItem> menuItems;
    
    public PancakeHouseMenu() {
        menuItems = new ArrayList<>();
        
        addItem("K&B 팬케이크 세트", "스크램블 에그와 토스트가 곁들어진 팬케이크", true, 2.99);
        addItem("레귤러 팬케이크 세트", "달걀 프라이와 소시지가 곁들어진 팬케이크", false, 2.99);
        addItem("블루베리 팬케이크", "블루베리와 블루베리 시럽으로 만든 팬케이크", true, 3.49);
        addItem("와플", "블루베리나 딸기를 올릴 수 있는 와플", true, 3.59);
    }
    
    public void addItem(String name, String description, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegeterian, price);
        menuItems.add(menuItem);
    }
    
    public ArrayList<MenuItem> getMenuItems() {
        return menuItems;
    }
}

 

식당의 메뉴들

public class DinerMenu {
    static final int MAX_ITEMS = 6;
    int numberOfItems = 0;
    MenuItem[] menuItems;
    
    public PancakeHouseMenu() {
        menuItems = new MenuItem[MAX_ITEMS];
        
        addItem("채식주의자용 BLT", "통밀, 콩고기 베이컨, 상추, 토마토", true, 2.99);
        addItem("BLT", "통밀, 베이컨, 상추, 토마토", false, 2.99);
        addItem("오늘의 스프", "감자 샐러드와 오늘의 스프", true, 3.49);
        addItem("핫도그", "사워크라우트, 양념, 양파, 치즈", true, 3.59);
    }
    
    public void addItem(String name, String description, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegeterian, price);
        
        if (numberOfItems >= MAX_ITEMS) {
            System.err.println("메뉴가 꽉 차 더 이상 추가할 수 없습니다.");
        } else {
            menuItems[numberOfItems ] = menuItem;
            numberOfItems += 1;
        }
    }
    
    public MenuItem[] getMenuItems() {
        return menuItems;
    }
}

 

 

두 가게가 서로 다른 컬렉션을 사용하고 있습니다. 두 컬렉션을 Iterator 인터페이스로 반환하는 메소드를 추가해줍시다.

 

PancakeHouseMenu에 메소드 추가

public Iterator<MenuItem> createIterator() {
    return menuItems.iterator();				// Java 컬렉션에서 제공하는 기본 Iterator
}

 

DinerMenu에 메소드 추가

public Iterator createIterator() {
    return new DinerMenuIterator(menuItems);
}

 

DinerMenuIterator 클래스 생성하기

public class DinerMenuIterator implements Iterator<MenuItem> {
    MenuItem[] items;
    int position = 0;
    
    public DinerMenuIterator(MenuItem[] items) {
        this.items = items;
    }
    
    public MenuItem next() {
        MenuItem menuItem = items[position];
        position += 1;
        return menuItem;
    }
    
    public boolean hasNext() {
        if (position >= items.length || items[position] == null) {
            return false;
        } else {
            return true;
        }
    }
    
    public void remove() {
        throw new UnsupportedOperationException("메뉴 항목을 지울 수 없습니다.");
    }
}

 

 

이제 종업원은 Iterator 인터페이스를 통해 두 가게의 메뉴판을 하나로 통합하여 사용할 수 있습니다.

public class Waitress {
    Menu pancakeHouseMenu;
    Menu dinerMenu;
    
    public Waitress(Menu pancakeHouseMenu, Menu dinerMenu) {
        this.pancakeHouseMenu = pancakeHouseMenu;
        this.dinerMenu = dinerMenu;
    }
    
    public void printMenu() {
        Iterator<MenuItem> pancakeIterator = pancakeHouseMenu.createIterator();
        Iterator<MenuItem> dinerIterator = dinerMenu.createIterator();
        System.out.println("MENU");
        printMenu(pancakeIterator);
        printMenu(dinerIterator);
    }
    
    private void printMenu(Iterator iter) {
        while (iter.hasNext()) {
            MenuItem menuItem = iter.next();
            System.out.print(menuItem.getName() + ", ");
        	...
        }
    }
}

 

 

 

 

 

헤드 퍼스트 디자인 패턴 - YES24

유지관리가 편리한 객체지향 소프트웨어 만들기!“『헤드 퍼스트 디자인 패턴(개정판)』 한 권이면 충분하다.이유 1. 흥미로운 이야기와 재치 넘치는 구성이 담긴 〈헤드 퍼스트〉 시리즈! 하나

www.yes24.com

 

반응형