반복자 패턴 (Iterator Pattern)
•
반복자 패턴은 컬럭션의 구현 방법을 노출하지 않으면서 집합체 내의 모든 항목에 접근하는 방법을 제공하는 패턴
•
이 패턴을 사용하면 집합체 내에서 어떤 식으로 일이 처리되는지 전혀 모르는 상태에서 그 안에 들어있는 모든 항목을 대상으로 반복 작업을 수행할 수 있음
•
반복자 패턴을 사용하면 모든 항목에 일일이 접근하는 작업을 컬럭션 객체가 아닌 반복자 객체가 맡게 됨
◦
집합체의 인터페이스와 구현이 간단해지고 집합체 반복 작업에서 손을 떼고 원래 자신이 할 일에만 전념할 수 있음
단일 역할 원칙
•
어떤 클래스가 바뀌는 이유는 하나뿐이어야 한다.
•
클래스를 고치는 일은 최대한 피해야 함
◦
코드를 변경할 만한 이유가 2가지나되면 그 만큼 그 클래스를 고쳐야할 가능성이 커지며 디자인에 있어서 2가지 부분에 영향을 미치게 됨
객체마을 식당 메뉴에 반복자 추가하기
•
객체마을 식당과 팬케이크 하우스가 합병되어 메뉴 구현항목이 서로 다르다는 문제점이 발생
◦
한쪽은 배열로, 한쪽은 ArrayList 컬렉션을 사용 함
public interface Iterator {
boolean hasNext();
MenuItem next();
}
public interface Menu {
Iterator createIterator();
}
Java
복사
메뉴의 반복을 캡슐화한 반복자 인터페이스
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;
}
}
}
Java
복사
DinerMenu 클리스에 사용할 구상 Iterator 클래스
public class DinerMenu implements Menu {
static final int MAX_ITEMS = 6;
int numberOfItems = 0;
MenuItem[] menuItems;
// 생성자
// addItem 호출
@Override
public Iterator createIterator() {
return new DinerMenuIterator(menuItems);
}
// 기타 메뉴 관련 메소드
}
Java
복사
반복자 패턴의 구상 클래스를 리턴하도록 변경
public class Waitress {
...
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());
}
}
...
}
Java
복사
하나의 반복자로 모든 메뉴를 출력할 수 있음!
Iterable 인터페이스 알아보기
•
어떤 클래스에서 Iterable을 구현한다면 그 클래스는 iterator() 메소드를 구현해야 함
•
그 메소드는 Iterator 인터페이스를 구현하는 반복자를 리턴함
•
이 인터페이스에는 컬렉션이 있는 항목을 대상으로 반복 작업을 수행하는 방법을 제공하는 forEach() 메소드가 기본으로 포함 됨
•
자바는 향상된 for 순황문으로 몇 가지 편리한 문법적 기능을 제공함
public class Waitress {
...
private void printMenu(Iterator iterator) {
for (MenuItem item : iterator) {
System.out.print(menuItem.getName() + ", ");
System.out.print(menuItem.getPrice() + " -- ");
System.out.println(menuItem.getDescription());
}
}
...
}
Java
복사
hasNext, next 메소드를 직접 사용하지 않아도 됨
컴포지트 패턴 (Composite)
•
컴포지트 패턴으로 객체를 트리구조로 구성해서 부분-전체 계층구조를 구현합니다.
•
컴포지트 패턴을 사용하면 클라이언트에서 개별 객체와 복합 객체를 똑같은 방법으로 다룰 수 있음
•
메뉴와 항목을 같은 구조에 넣어서 부분-전체 계증 구조를 생성할 수 있음
•
컴포지트 패턴을 사용하면 객체의 구성와 개별 객체를 노드로 가지는 트리 형태의 객체 구조를 만들 수 있음
•
이런 복합구조를 사용하면 복합 객체와 개별객체를 대상으로 똑같은 작업을 적용할 수 있음
•
즉, 복합 객체와 개별 객체를 구분할 필요가 거의 없어짐
public abstract class MenuComponent {
public void add(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public void remove(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public MenuComponent getChild(int i) {
throw new UnsupportedOperationException();
}
public String getName() {
throw new UnsupportedOperationException();
}
public String getDescription() {
throw new UnsupportedOperationException();
}
public double getPrice() {
throw new UnsupportedOperationException();
}
public boolean isVegetarian() {
throw new UnsupportedOperationException();
}
public void print() {
throw new UnsupportedOperationException();
}
}
Java
복사
메뉴 컴포지트 추상클래스
public class MenuItem extends MenuComponent {
String name;
String description;
boolean vegetarian;
double price;
public MenuItem(String name,
String description,
boolean vegetarian,
double price)
{
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public double getPrice() {
return price;
}
public boolean isVegetarian() {
return vegetarian;
}
public void print() {
System.out.print(" " + getName());
if (isVegetarian()) {
System.out.print("(v)");
}
System.out.println(", " + getPrice());
System.out.println(" -- " + getDescription());
}
}
Java
복사
추상 클래스의 구현 클래스 Leaf 노드에 해당함
public class Menu extends MenuComponent {
ArrayList<MenuComponent> menuComponents = new ArrayList<MenuComponent>();
String name;
String description;
public Menu(String name, String description) {
this.name = name;
this.description = description;
}
public void add(MenuComponent menuComponent) {
menuComponents.add(menuComponent);
}
public void remove(MenuComponent menuComponent) {
menuComponents.remove(menuComponent);
}
public MenuComponent getChild(int i) {
return (MenuComponent)menuComponents.get(i);
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public void print() {
System.out.print("\n" + getName());
System.out.println(", " + getDescription());
System.out.println("---------------------");
// 재귀적으로 자신의 메뉴 정보를 출력
for (MenuComponent menuComponent : menuComponents) {
menuComponent.print();
}
}
}
Java
복사
public class Waitress {
MenuComponent allMenus;
public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
}
public void printMenu() {
allMenus.print();
}
}
Java
복사