Java 언어에서 배열의 모든 요소를 표시하기 위해서는 아래와 같이 사용합니다.
for (int i = 0; i < arr.length; i++) {
Sysytem.out.println(arr[i]);
}
보통 위와 같은 코드를 사용 할 겁니다.
위에서 사용 되고 있는 i를 살펴보죠. 변수 i는 처음에 0으로 초기화되어 1, 2, 3, 4,...으로 증가하게 되는데요.
그 때마다 arr[i]의 내용이 출력되죠. 이와 같은 형식의 for문은 자주 사용 하는데요.
특히 위와 같은 for문을 사용하면 변수 i가 인덱스 값이 되어 배열 안에 있는 특정 요소를 가져 올 수 있다는 점인데요.
arr[0] // 0번째 요소
arr[1] // 1번째 요소
arr[2] // 2번째요소
...
arr[i] // i번째 요소
arr[arr.length - 1] // 배열 마지막 요소
보통 이렇게 데이터를 가져올 수 있죠. 배열과 for문을 사용해보신 분들이라면 굉장히 익숙한 형태일겁니다.
여기에서 사용되고 있는 변수 i의 기능을 추상화해서 일반화한 것을 디자인 패턴에서는 Iterator패턴 이라고 부릅니다.
그러면 Iterator패턴은 무엇일까요? 책이나 문서에서는 아래와 같이 설명 되고 있습니다.
무엇인가 많이 모여있는 것들을 순서대로 지정하면서 전체를 검색하는 처리를 실행하기 위한 것.
Iterator는 무엇인가를 반복한다라는 의미이고, 반복자라고도 합니다. 이번에는 이 Iterator 패턴에 대해 알아보겠습니다.
보통 Iterator 패턴을 설명할 때 주로 사용하는 예제가 어떤 것을 나열해서 그 나열된 것들을 검색하는 프로그램들을 예를 드는데
냉장고 안에 음료수가 있다 가정하고, 그 음료수들을 차례대로 표시하는 프로그램을 만들어보죠.
먼저 클래스 다이어그램을 그래보면
위와 같이 그려집니다.
그림으로 그려보면 아래와 같이 설명할 수 있는데요.
Aggregate는 어떤 집합체를 나타내는 인터페이스가 되겠고, Iterator는 음료들을 하나씩 나열하면서 검색하는 인터페이스가 되고,
Drink은 말 그대로 음료수 클래스고, Refrigerator는 냉장고 클래스, DrinkIterator는 음료들을 검색하는 클래스가 됩니다.
그리고 이 들의 동작을 테스트 하는 Main 클래스가 필요하겠네요!
이 때 사용 되는 인터페이스가 Aggregate 인터페이스 인데요. 요소들이 나열되어 있는 집합체를 나타내요.
이 인터페이스를 구현하고 있는 클래스는 배열과 같이 무엇인가가 많이 모여 있는데요.
Aggregate는 사전적인 의미로 모으다, 모이다, 집합 이라는 의미로 쓰여 있습니다.
이제 차례대로 각 인터페이스와 클래스를 작성해볼까요?
먼저 Aggregate 인터페이스입니다. Aggregate.java파일을 생성하여 작성 해주시면 됩니다.
package bami;
public interface Aggregate {
public abstract Iterator iterator();
}
이 인터페이스에서 선언되고 있는 메스드는 iterator 하나 뿐입니다. 집합체에 대응하는 Iterator를 작성하기 위해 사용했습니다.
이 처럼 집합체를 하나씩 나열하고, 검색하고, 조사하고 싶을 때는 iterator 메소드를 사용해서 Iterator 인터페이스를 구현한 클래스의 인스턴스를 만듭니다.
그 다음으로 Iterator 인터페이스를 작성해 볼 것인데요. 아까도 설명드렸지만 Iterator는 요소를 하나씩 나열하면서 루프 변수와 같은 역할을 해준다고 설명 드렸습니다. 이 인터페이스도 마찬가지 입니다.
그 중에서 가장 단순한 형태로 만들어 보겠습니다. Iterator.java파일을 생성 하신 뒤에 작성해주세요.
package bami;
public interface Iterator {
public abstract boolean hasNext();
public abstract Object next();
}
여기에서 선언되어 있는 메소드는 다음 요소가 존재하는 지를 알아내는 hasNext 메소드와 다음 요소를 얻기 위한 next 메소드입니다.
냉장고 안에 다음 음료수가 존재하면 hasNext는 true를 반환할 것이고, 음료수가 존재하지 않는다면(마지막 요소라면) false를 반환되어 하나의 루프가 종료 될 것입니다.
next 메소드는 리턴값이 Object이기 때문에 집합체의 요소 1개를 리턴해줍니다. 뿐만 아니라 다음 next 메소드를 호출 했을 때 다음 요소를 정확하게 반환하도록 진행시켜 두는 역할도 있습니다. 구체적인 부분은 이따 DrinkIterator를 작성할 때 알아보도록 하죠.
그 다음은 음료수를 나타내는 Drink 클래스를 작성해 볼 것입니다. 마찬가지로 Drink.java 파일을 생성하여 작성하여 주세요.
public class Drink {
private String name;
public Drink(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
이 클래스의 역할은 음료수 이름을 getName()에서 얻는 역할 뿐입니다. 음료수 이름은 생성자에서 인스턴스를 초기화 할 때 인수로 지정했습니다.
그 다음 Refrigerator클래스를 작성해보죠. 마찬가지로 파일을 생성하여 작성해주세요.
package bami;
public class Refrigerator implements Aggregate {
private Drink[] drink;
private int lastIndex = 0;
public Refrigerator(int maxsize) {
this.drink = new Drink[maxsize];
}
public Drink getDrinkAt(int index) {
return drink[index];
}
public void appendDrink(Drink drink) {
this.drink[lastIndex] = drink;
lastIndex++;
}
public int getLength() {
return lastIndex;
}
public Iterator iterator() {
return new DrinkIterator(this);
}
}
이 냉장고 클래스는 Drink의 배열인 drinks라는 필드를 가지고 있습니다.
maxsize는 Drink배열의 크기를 나타내는데 처음에 refrigerator의 인스턴스를 만들 때 지정해줍니다.
그리고 drinks, lastIndex를 private로 지정하여 외부로부터 의도치 않게 변경되는 것을 막았습니다.
여기에서 iterator 메소드는 DrinkIterator 클래스에 대응하는 Iterator인데, DrinkIterator 클래스의 인스턴스를 생성해서 반환해줍니다. 냉장고 안에 음료수를 하나씩 나열하기 위해 사용했습니다.
그 다음은 DrinkIterator 클래스를 작성해 봅시다. 마찬가지로 파일을 생성하여 작성해주세요.
package bami;
public class DrinkIterator implements Iterator {
private Refrigerator refrigerator;
private int index;
public DrinkIterator(Refrigerator refrigerator) {
this.refrigerator = refrigerator;
this.index = 0;
}
public boolean hasNext() {
if (index < refrigerator.getLength()) {
return true;
} else {
return false;
}
}
public Object next() {
Drink drink = refrigerator.getDrinkAt(index);
index++;
return drink;
}
}
DrinkIterator를 Iterator로 다루기 위해 Iterator 인터페이스를 구현하도록 하였습니다.
refrigerator 필드는 Refrigerator가 검색할 냉장고이고, index 필드는 현재 가리키고 있는 음료수를 의미합니다.
생성자에서는 전달된 Refrigerator의 인스턴스를 refrigerator 필드에 저장하고 index를 0으로 해줍니다.
hasNext 메소드는 Iterator 인터페이스에서 선언되어 있는 메소드를 구현 한 것으로 하고, 다음 음료수가 존재하면 true, 존재하지 않으면 false를 반환해줍니다. 다음 음료수의 존재 여부는 refrigerator.getLength()의 값과 비교하여 판단합니다.
next 메소드는 현재 처리하고 있는 음료수를 반환하고, 다음으로 진행시키기 위한 메소드 입니다.
Iterator 인터페이스에서 선언되어 있는 메소드이고, 리턴값으로 리턴해야 할 음료수를 drink라는 변수로 저장하고, index를 1증가 시켜 다음으로 진행시킵니다.
이제 냉장고 안에 있는 음료수를 검색할 준비가 끝났는데요. 이제 이 것을 테스트 해보기 위해 main 클래스를 만들어 볼 것인데요.
이 클래스를 사용해서 5개의 음료수가 들어 있는 냉장고를 만들어 봅시다!
package bami;
public class main {
public static void main (String[] args) {
Refrigerator refrigerator = new Refrigerator(5);
refrigerator.appendDrink(new Drink("콜라"));
refrigerator.appendDrink(new Drink("환타"));
refrigerator.appendDrink(new Drink("웰치스"));
refrigerator.appendDrink(new Drink("박카스"));
refrigerator.appendDrink(new Drink("토레타"));
Iterator it = refrigerator.iterator();
while (it.hasNext()) {
Drink drink = (Drink)it.next();
System.out.println(drink.getName());
}
}
}
이렇게 해서 콜라, 환타, 웰치스, 박카스, 토레타 가 들어간 냉장고를 만들어 봤습니다. 원하시는 음료수를 넣어주시면 됩니다.
만약 음료수를 더 집어넣고 싶으시다면 Refrigerator refrigerator = new Refrigerator(5); 부분의 숫자를 원하시는 만큼 늘려주시면 됩니다.
냉장고 안에 있는 음료수를 검색하기 위해 it변수를 사용하였고, while 조건 부분에 it.hasNext를 사용하여 false를 반환 할 때 종료 되도록 만들었습니다.
it.hasNext()가 true일 때 it.next()에 의해 음료수 하나씩 검사하게 되고, 검사한 음료수가 출력 됩니다.
그 후 false가 되면 while문을 벗어나 프로그램이 종료 되게 됩니다.
실행 결과는 아래와 같습니다.
풀소스
Aggregate.java
package bami;
public interface Aggregate {
public abstract Iterator iterator();
}
Drink.java
package bami;
public class Drink {
private String name;
public Drink(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
DrinkIterator.java
package bami;
public class DrinkIterator implements Iterator {
private Refrigerator refrigerator;
private int index;
public DrinkIterator(Refrigerator refrigerator) {
this.refrigerator = refrigerator;
this.index = 0;
}
public boolean hasNext() {
if (index < refrigerator.getLength()) {
return true;
} else {
return false;
}
}
public Object next() {
Drink drink = refrigerator.getDrinkAt(index);
index++;
return drink;
}
}
Iterator.java
package bami;
public interface Iterator {
public abstract boolean hasNext();
public abstract Object next();
}
Refrigerator.java
package bami;
public class Refrigerator implements Aggregate {
private Drink[] drink;
private int lastIndex = 0;
public Refrigerator(int maxsize) {
this.drink = new Drink[maxsize];
}
public Drink getDrinkAt(int index) {
return drink[index];
}
public void appendDrink(Drink drink) {
this.drink[lastIndex] = drink;
lastIndex++;
}
public int getLength() {
return lastIndex;
}
public Iterator iterator() {
return new DrinkIterator(this);
}
}
main.java
package bami;
public class main {
public static void main (String[] args) {
Refrigerator refrigerator = new Refrigerator(5);
refrigerator.appendDrink(new Drink("콜라"));
refrigerator.appendDrink(new Drink("환타"));
refrigerator.appendDrink(new Drink("웰치스"));
refrigerator.appendDrink(new Drink("박카스"));
refrigerator.appendDrink(new Drink("토레타"));
Iterator it = refrigerator.iterator();
while (it.hasNext()) {
Drink drink = (Drink)it.next();
System.out.println(drink.getName());
}
}
}
참고 : Java 언어로 배우는 디자인 패턴 입문
'프로그래밍(Basic) > 디자인 패턴(Java)' 카테고리의 다른 글
[바미] Singleton 패턴에 대해 알아봅시다. (0) | 2021.09.23 |
---|---|
[바미] Factory Method에 대해 알아봅시다. (0) | 2021.09.21 |
[바미] 템플릿 메소드(Template Method) 패턴에 대해 알아봅시다. (0) | 2021.09.15 |
[바미] Adapter 패턴에 대해 알아봅시다. (0) | 2021.08.24 |
[바미] UML에 대해 알아봅시다. (0) | 2021.08.16 |