////
Search

9장 - 반복자 패턴과 컴포지드 패턴

Created
2022/09/03 11:30
Tags
디자인패턴

반복자 패턴 (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
복사