두 개의 구성 객체에서 하나는 정보를 ArrayList
데이터 타입로 가지고 있고 하나는 정보를 Array
데이터 타입으로 가지고 있을 경우, 아래와 같이 각각 다른 loop
를 통해 탐색을 해야합니다.
ArrayList
- size()
함수 이용Array
- 배열에 들어있는 원소의 크기 변수 이용Waitress.java
public void printMenu() {
ArrayList breakfastItems = pancakeHouseMenu.getMenuItems();
for (int i = 0; i < breakfastItems.size(); i++) {
MenuItem menuItem = (MenuItem) breakfastItems.get(i);
System.out.print(menuItem.getName() + " ");
System.out.println(menuItem.getPrice() + " ");
System.out.println(menuItem.getDescription());
}
MenuItem[] lunchItems = dinerMenu.getMenuItems();
for (int i = 0; i < dinerMenu.numberOfItems; i++) {
MenuItem menuItem = lunchItems[i];
System.out.print(menuItem.getName() + " ");
System.out.println(menuItem.getPrice() + " ");
System.out.println(menuItem.getDescription());
}
}
HashMap
형태의 다른 데이터 타입이 또 추가된다면 loop
를 돌기 위해 또다른 방식의 처리를 해줘야 합니다.
매번 다른 형태의 데이터타입을 가지고 있는 객체에 대해서 위처럼 처리를 해주는 것을 개선하기 위해 반복을 캡슐화한 이터레이터 패턴(Iterator Pattern)을 적용합니다.
각각의 구성 객체(PancakeHouseMenu
, DinerMenu
)에 Iterator
를 구현한 객체를 하나씩 구성하도록 개선하였습니다. 그럼 아래와 같이 loop
를 도는 부분을 캡슐화하여 통일감 있게 코드를 작성할 수 있습니다.
Waitress.java
public void printMenu() {
Iterator pancakeIterator = pancakeHouseMenu.createItertor();
Iterator dinerIterator = dinerMenu.createIterator();
printMenu(pancakeIterator);
printMenu(dinerIterator);
}
public void printMenu(Iterator iterator) {
while(iterator.hasNext()) {
MenuItem menuItem = (MenuItem) iterator.next();
System.out.print(menuItem.getName() + " ");
System.out.println(menuItem.getPrice() + " ");
System.out.println(menuItem.getDescription());
}
이렇게 개선하면, 각 객체가 어떤 타입의 데이터(ArrayList, Array, HashMap
등)를 가지고 있는지 Waitress
에서 알 필요 없도록 캡슐화하고, Waitress
에서는 Iterator
객체만 알고 있으면 됩니다.
ArrayList
와 HashMap
의 경우, Java의 Iterator
를 포함하고 있습니다. 따라서 이전의 reference code 에서 PancakeIterator
를 구현할 필요가 없이 아래와 같이 수정할 수 있습니다.
PancakeHouseMenu.java
// Java Iterator 적용 전
public Iterator createItertor() {
return new PancakeIterator(menuItems);
}
// Java Iterator 적용 후
public Iterator createItertor() {
return menuItems.iterator();
}
Array
의 경우에는 포함하고 있지 않기 때문에 이를 구현해줘야 합니다.
다중 스레드를 사용하는 환경에서는 같은 객체 컬렉션(Collection
)에 대해 여러 반복자가 있는 경우, remove()
함수에 대한 처리가 정의되어 있지 않습니다. 따라서 컬렉션에 동시에 접근하는 멀티스레드 코드를 디자인할 때는 매우 조심해야 합니다.
이터레이터 패턴(Iterator Pattern)은 컬렉션 구현 방법을 노출시키지 않으면서도 그 집합체 안에 들어있는 모든 항목에 접근할 수 있게 해주는 방법을 제공해줍니다.
클래스에서 맡고 있는 역할들은 나중에 코드 변화를 불러올 수 있습니다.
Design Principal 9.
클래스를 바꾸는 이유는 한 가지 뿐이어야 한다.
클래스를 고치는 것은 최대한 피해야 합니다. 코드를 변경하다 보면 온갖 문제가 발생할 수 있기 대문입니다. 따라서 변화를 최소화하기 위해 한 클래스에서는 응집도 높은 메소드만 담당하고 있는 것이 좋습니다.