책너두 (헤드 퍼스트 디자인 패턴) 27일차 (~373p)

요약

  • 반복자 패턴과 컴포지트 패턴
    • 반복자 패턴
      • 객체마을 식당 메뉴에 반복자 추가하기
      • 객체마을 식당 메뉴에서 반복자 사용하기
      • 종업원 코드에 반복자 적용하기
    • 반복자 패턴의 특징 알아보기
      • 인터페이스 개선하기
      • java.util.Iterator 적용하기
    • 반복자 패턴의 정의
    • 반복자 패턴의 구조 알아보기

메모

반복자 패턴

  • 반복자(iterator) 패턴이라고 부름.
    • Iterator 인터페이스에 의존함.

  • hasNext() 메소드를 사용하면 반복 작업을 적용할 대상이 더 있는지 확인 가능
  • next() 메소드는 다음 객체를 리턴함.
  • 이 인터페이스가 있으면 배열, 리스트, 해시 테이블은 물론, 모든 종류의 객체 컬렉션에 반복자를 구현할 수 있음.

객체마을 식당 메뉴에 반복자 추가하기

public interface Iterator {
    boolean hasNext();
    MenuItem next();
}
  • Iterator 인터페이스를 정의함.
public class DinerMenuIterator implements Iterator {
    MenuItem[] items;
    int position = 0;

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

    public MenuItem next() {
        MenuItem menuItem = items[position];
        position position + 1;
        return menuItem;
    }

    public boolean hasNext() {
        if (position >= items.length || items[position] == null) {
            return false;
        } else {
            return true;
        }
    }
}
  • position 필드로 반복 작업이 처리되고 있는 위치를 저장함.
  • MenuItem 이 배열이므로, hasNext 메소드에서 끝인지 확인하는 것 뿐만 아니라, 중간이더라도, 다음 항목이 null, 즉 원소가 없다면 false 를 던져줘야 함.

객체마을 식당 메뉴에서 반복자 사용하기

public class DinerMenu {
    static final int MAX_ITEMS = 6;
    int numberOfItems = 0;
    MenuItem[] menuItems;

    // 생성자

    // addItem 메소드 호출

    // getMenuItems 메소드는 더 이상 필요 없음. (이제 굳이 내부 구조를 드러내지 않아도 됨)

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

    // 기타 메뉴 관련 메소드
}
  • createIterator 메소드는 Iterator 인터페이스를 리턴함.
    • 클라이언트는 menuItem 이 어떻게 관리되는지 알 필요 없음.
    • 그냥 반복자로 메뉴에 들어있는 항목 하나하나에 접근할 수만 있으면 됨.

종업원 코드에 반복자 적용하기

public class Waitress {
    PancakeHouseMenu pancakeHouseMenu;
    DinerMenu dinerMenu;

    public Waitress (PancakeHouseMenu pancakeHouseMenu, DinerMenu diner Menu) {
        this.pancakeHouseMenu = pancakeHouseMenu;
        this.diner Menu = diner Menu;
    }

    public void printMenu() {
        Iterator pancakeIterator = pancakeHouseMenu.createIterator();
        Iterator dinerIterator = dinerMenu.createIterator();

        System.out.println("\n----\n");
        printMenu(pancakeIterator);
        System.out.println("\n");
        printMenu(dinerIterator);
    }

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

    // 기타 메소드
}
  • printMenu 메소드는 Iterator 를 파라미터로 받기 때문에, 메뉴가 어떤 방식으로 구현되어있든 항목에 손쉽게 접근 가능하다.

반복자 패턴의 특징 알아보기

  • 메뉴 구현법이 캡슐화되어 있음.
    • 종업원은 메뉴에서 메뉴 항목의 컬렉션을 어떤 식으로 저장하는지 알 필요 없음.
  • 반복자만 구현한다면 다형성을 활용해서 어떤 컬렉션이든 1개의 순환문으로 처리할 수 있음.
  • 종업원은 인터페이스(반복자)만 알면 됨.
  • Menu 인터페이스가 완전히 똑같지 않은 상태임. (구상 클래스가 그대로 각각의 역할을 함)
    • 2개의 구상 메뉴 클래스에 묶여 있음.
      • PancakeHouseMenu
      • DinerMenu
        • 두 클래스의 인터페이스가 완전히 똑같음에도 아직 인터페이스 통일하지는 않음.

인터페이스 개선하기

  • PancakeHouseMenu, DinerMenu 인터페이스가 완전히 똑같음에도 아직 인터페이스를 통일하지 않음.
  • java.util 에는 Iterator 인터페이스를 제공해주고 있음.
    • ArrayList 에는 반복자를 리턴하는 iterator() 메소드도 있음.
    • 하지만 배열로 구현된 DinerMenu는 따로 반복자를 구현해야 함.

java.util.Iterator 적용하기

  • PancakeHouseMenuIterator 클래스를 지우고, PancakeHouseMenu 에 java.util.Iterator 를 임포트하고, 코드 1줄만 고치면 됨.
public Iterator<MenuItem> createIterator() {
    return menuItems.iterator();
}
  • ArrayList의 iterator() 메소드만 호출하면 됨.
  • DinnerMenuIterator 의 경우, 배열로 구현되어있기 때문에 Iterator 를 구현해야 함.
    • 위에서 구현한 DinerMenuIterator 에서 바꿀건 없고, remove 메소드만 오버라이드해서, UnsupportedOperationException 을 던져주면 됨.
public interface Menu {
    public Iterator<MenuItem> createIterator();
}
  • 메뉴 인터페이스를 통일하고, 종업원 코드를 조금만 고치면 됨.
import java.util.Iterator;

public class Waitress {
    Menu pancakeHouseMenu;
    Menu diner Menu;

    public Waitress (Menu pancakeHouseMenu, Menu diner Menu) {
        this.pancakeHouseMenu = pancakeHouseMenu;
        this.diner Menu = diner Menu;
    }

    public void printMenu() {
        Iterator<MenuItem) pancakeIterator = pancakeHouseMenu.createIterator();
        Iterator<MenuItem) dinerIterator = diner Menu.createIterator();
        System.out.println("MENU\n----\n");
        printMenu(pancakeIterator);
        System.out.println("\n");
        printMenu(dinerIterator);
    }

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

    // 기타 메소드
}
  • PancakeHouseMenu, DinerMenu 클래스를 Menu 인터페이스로 통일함.

반복자 패턴의 정의

  • 각 항목에 일일이 접근할 수 있게 해 주는 기능을 집합체가 아닌 반복자 객체가 책임진다는 장점도 있음.
  • 그러면서 집합체 인터페이스와 구현이 간단해지고, 각자에게 중요한 일만을 처리할 수 있게 됨.

반복자 패턴의 구조 알아보기

  • Arregate 공통 인터페이스가 있으면 클라이언트는 매우 편리하게 작업을 처리할 수 있음.
  • ConcreteArregate 에는 객체 컬렉션이 들어있음.
    • 그 안에 들어있는 컬렉션을 Iterator로 리턴하는 메소드를 구현함.
    • 안에있는 객체 컬렉션을 대상으로 돌아가면서 반복 작업을 처리할 수 있게 해주는 ConcreateIterator 인스턴스를 만들 수 있어야 함.
  • ConcreateIterator 는 반복 작업 중에 현재 위치를 관리하는 일을 맡음.
  • Iterator 인터페이스는 모든 반복자가 구현해야 하는 인터페이스를 제공함.

댓글

Designed by JB FACTORY