개발 도서
디자인 패턴 - 반복자 패턴
백수왕
2023. 12. 19. 15:19
반복자 패턴
컬렉션의 구현 방법을 노출하지 않으면서 집합체 내의 모든 항목에 접근하는 방법을 제공합니다.
(여기서 말하는 집합체란 동일한 형태의 데이터 항목을 여러개 가지고 있는 것을 말합니다.)
즉, 반복자 패턴은 집합체를 쉽게 순회하고 접근할 수 있도록 하는 디자인 패턴입니다.
특징
집합체의 구현을 외부에 노출하지 않고, 반복에 필요한 인터페이스만을 제공하여 캡슐화를 강화합니다.
장점
- 유연성과 확장성 : 집합체의 구조를 바꾸거나, 새로운 집합체를 추가해도 클라이언트 코드에 영향을 주지 않으면서 반복 동작을 구현할 수 있습니다.
- 단일 책임의 원칙 : 집합체의 관리와 반복에 대한 책임이 분리되어 각각 단일 책임의 원칙을 지킬 수 있습니다.
단점
- 간단한 집합체에 비효율적 : 작은 규모의 경우에는 반복자 패턴을 사용하는 것이 비효율적일 수 있습니다.
- 추가 클래스 및 복잡성 : 반복자 패턴을 구현하려면 새로운 반복자 클래스를 만들어야 하므로 클래스의 수가 증가할 수 있어 더 복잡해질 수 있습니다.
반복자 패턴 예제
StarCafe가 바로 옆에 있는 MoonCafe를 인수한 상황입니다.
public class Coffee {
private String name;
private int price;
public Coffee(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
}
StarCafe와 MoonCafe는 주문 시스템이 달랐습니다.
public class StarCafe {
Coffee[] coffeeArray = new Coffee[10];
int orderCount = 0;
public void orderCoffee(String name, int price) {
Coffee coffee = new Coffee(name, price);
if (orderCount >= 10) {
System.out.println("한 번에 10개 이상 주문할 수 없습니다.");
} else {
coffeeList[orderCount++] = coffee;
}
}
public Coffee[] getOrderCoffee() {
return coffeeList;
}
public int getOrderCount() {
return orderCount;
}
}
StarCafe는 배열을 사용하였고
public class MoonCafe {
List<Coffee> coffeeList = new ArrayList<>();
public void orderCoffee(String name, int price) {
Coffee coffee = new Coffee(name, price);
coffeeList.add(coffee);
}
public List<Coffee> getOrderCoffee() {
return coffeeList;
}
}
MoonCafe는 리스트를 사용합니다.
그래서 주문 시스템을 새로 만들기로 하는데 그러기엔 비용이 너무 많이 든다고 생각해 일단 기존 방식에 추가하기로 합니다.
public class Kiosk {
StarCafe starCafe;
MoonCafe moonCafe;
public Kiosk(StarCafe starCafe, MoonCafe moonCafe) {
this.starCafe = starCafe;
this.moonCafe = moonCafe;
}
public void orderStarCafe() {
for (int i = 0; i < starCafe.getOrderCount(); i++) {
Coffee[] coffeeList = starCafe.getOrderCoffee();
System.out.print(coffeeList[i].getName() + " ");
System.out.println(coffeeList[i].getPrice() + "원");
}
for (int i = 0; i < moonCafe.coffeeList.size(); i++) {
List<Coffee> coffeeList = moonCafe.getOrderCoffee();
System.out.print(coffeeList.get(i).getName() + " ");
System.out.println(coffeeList.get(i).getPrice() + "원");
}
}
}
하지만 예제에서 알 수 있듯 반복문이 2번 돌아가게 됩니다.
그래서 이 방법을 해결하기 위해 Iterator를 사용하기로 합니다.
public class MoonCafe {
// 추가
public Iterator<Coffee> getIterator() {
return coffeeList.listIterator();
}
}
MoonCafe는 List를 사용하고 있었는데 List는 Iterator를 구현하고 있기 때문에 조금의 코드만 추가해주면 됩니다.
public class StarCafeIterator implements Iterator<Coffee> {
private Coffee[] coffeeList;
private int currentIndex = 0;
public StarCafeIterator(Coffee[] coffeeList) {
this.coffeeList = coffeeList;
}
@Override
public boolean hasNext() {
return currentIndex < coffeeList.length && coffeeList[currentIndex] != null;
}
@Override
public Coffee next() {
return coffeeList[currentIndex++];
}
}
하지만 StarCafe는 배열을 사용하고 있기 때문에 위 클래스를 또 따로 만들어 줍니다
public class StarCafe {
// 추가
public Iterator<Coffee> getIterator() {
return new StarCafeIterator(coffeeArray);
}
}
StarCafe 클래스에 새로 만든 클래스를 호출하는 메서드를 만들어 주면 됩니다.
그리고 kiosk에서 Iterator을 사용하여 반복문을 돌려주면 됩니다.
public void orderCoffee() {
Iterator<Coffee> starCafeIter = starCafe.getIterator();
Iterator<Coffee> moonCafeIter = moonCafe.getIterator();
orderCoffeeIter(starCafeIter);
orderCoffeeIter(moonCafeIter);
}
public void orderCoffeeIter(Iterator<Coffee> iterator) {
while (iterator.hasNext()) {
Coffee coffee = iterator.next();
System.out.print(coffee.getName() + " ");
System.out.println(coffee.getPrice() + "원");
}
}
}