반복자 패턴(Iterator Pattern)

wannabeking·2022년 10월 15일
0

디자인 패턴

목록 보기
11/14
post-thumbnail

해당 예시는 헤드 퍼스트 디자인 패턴을 참고했습니다.

배열 메뉴 햄버거 가게와 리스트 메뉴 카페

햄버거 가게와 카페는 같은 공간에 존재했었는데, 햄버거를 먹고 카페에서 주문하는 손님이 많아지자 콜라보를 진행하기로 결정했다.

따라서 메뉴판을 통일시키기로 결정하였는데 문제가 발생했다.

햄버거 가게는 메뉴를 배열로 저장했는데, 카페는 리스트로 저장했다.

객체지향 종업원은 메뉴판에 메뉴를 출력할 때 햄버거 가게는 배열을 통해, 카페는 리스트를 통해 직접 반복하는 것이 마음에 들지 않아 리팩토링을 진행하기로 마음 먹었다.

종업원은 반복자 패턴을 사용하기로 하였다.



반복자 패턴

배열과 리스트를 순회하는 방법은 다르다.

보통의 경우 배열은 인덱스 i를 증가시키면서 arr[i]로 접근하고, 리스트는 get(i)라는 메소드를 통하여 접근한다.

반복자 패턴은 반복 작업을 캡슐화하는 인터페이스를 통해 반복의 대상이 어떻게 구현되었는지 노출하지 않으면서 모든 항목에 접근하는 방법을 제공하여 이를 해결한다.

코드를 살펴보자.


public class BurgerMenu implements Menu {

    public static final int MAX_ITEMS = 3;
    private int numOfItems = 0;
    private MenuItem[] menuItems;

    public BurgerMenu() {
        menuItems = new MenuItem[MAX_ITEMS];
    }

    @Override
    public void addItem(MenuItem menuItem) {
        if (numOfItems >= MAX_ITEMS) {
            System.out.println("메뉴가 꽉 찼습니다.");
        } else {
            menuItems[numOfItems++] = menuItem;
        }
    }

    @Override
    public Iterator iterator() {
        return new BurgerMenuIterator(menuItems);
    }
}
public class CafeMenu implements Menu {

    private List<MenuItem> items;

    public CafeMenu() {
        items = new ArrayList<>();
    }

    @Override
    public void addItem(MenuItem menuItem) {
        items.add(menuItem);
    }

    @Override
    public Iterator iterator() {
        return new CafeMenuIterator(items);
    }
}

햄버거 메뉴는 길이가 정해진 배열, 카페 메뉴는 리스트로 메뉴 아이템을 저장하고 있다.

여기서 주목할 것은 iterator() 메소드이며 반복자를 반환한다.

반복자를 살펴보자.


public interface Iterator {

    MenuItem next();

    boolean hasNext();
}

Iterator는 item을 순회하기 위한 인터페이스를 제공한다.

제공하는 메소드는 next()hasNext()로, 각각 다음 item을 반환하는 것과 다음 item이 존재하는지 여부를 반환하는 메소드이다.

public class BurgerMenuIterator implements Iterator {

    MenuItem[] items;
    int pos = 0;

    public BurgerMenuIterator(MenuItem[] items) {
        this.items = items;
    }

    @Override
    public MenuItem next() {
        if (!hasNext()) {
            throw new RuntimeException("no more item");
        }
        return items[pos++];
    }

    @Override
    public boolean hasNext() {
        return pos < items.length && items[pos] != null;
    }
}

햄버거 집은 배열을 사용하므로, 그에 맞게 next()hasNext()를 구현했다.

public class CafeMenuIterator implements Iterator {

    List<MenuItem> items;
    int pos = 0;

    public CafeMenuIterator(List<MenuItem> items) {
        this.items = items;
    }

    @Override
    public MenuItem next() {
        if (!hasNext()) {
            throw new RuntimeException("no more item");
        }
        return items.get(pos++);
    }

    @Override
    public boolean hasNext() {
        return pos < items.size();
    }
}

카페 메뉴도 리스트를 사용하여 알맞게 구현했다.

이제 종업원은 각 메뉴의 iterator()를 호출하여 반복자를 통해 메뉴판을 손님에게 전해줄 수 있다

종업원 클래스를 살펴보자.


public class Waitress {

    Menu burgerMenu;
    Menu cafeMenu;

    public Waitress(Menu burgerMenu, Menu cafeMenu) {
        this.burgerMenu = burgerMenu;
        this.cafeMenu = cafeMenu;
    }

    public void printMenu() {
        Iterator burgerIterator = burgerMenu.iterator();
        Iterator cafeIterator = cafeMenu.iterator();
        System.out.println("--버거 메뉴--");
        printMenu(burgerIterator);
        System.out.println("--카페 메뉴--");
        printMenu(cafeIterator);
    }

    private void printMenu(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = iterator.next();
            System.out.println(menuItem.getName() + " | " + menuItem.getCost() + " | "
                + menuItem.getDescription());
        }
    }
}

종업원은 메뉴가 어떻게 구현되었는지는 관심 없다.

다만 iterator()를 호출하여 반복자를 얻은 뒤 반복자를 통해 메뉴를 출력할 뿐이다.

따라서 앞서 반복자 패턴에 대해 설명한 것처럼, 반복 대상의 구현 방법을 노출하지 않고 순회할 수 있는 것이다.

마지막으로 psvm에서 출력을 확인하자.


public class Main {

    public static void main(String[] args) {
        Menu burgerMenu = new BurgerMenu();
        Menu cafeMenu = new CafeMenu();

        burgerMenu.addItem(new MenuItem("새우 버거", 3000, "새우가 들어간 버거"));
        burgerMenu.addItem(new MenuItem("불고기 버거", 4000, "한국인의 국민 버거"));
        cafeMenu.addItem(new MenuItem("아메리카노", 4000, "물처럼 마시는 커피"));
        cafeMenu.addItem(new MenuItem("라떼", 5000, "라떼는 말이야"));

        Waitress waitress = new Waitress(burgerMenu, cafeMenu);
        waitress.printMenu();
    }
}


모든 소스코드는 여기에서 확인할 수 있다.



profile
내일은 개발왕 😎

0개의 댓글