본문 바로가기

Design Pattern

[디자인 패턴_Again] Iterator Pattern.


각각의 콜렉션 객체에 대해서 for문이나 뭐 이런식으로 콜렉션안에 들어가 있는

객체들을 참조 할 수 있지만, Iterator를 사용하면 소스의 구분없이

컬렉션안의 객체들을 참조 할 수 있습니다.

예를 들어서 ArrayList를 사용해서 객체를 관리하는 프로그램과 (혹은 클래스)

배열을 사용해서 객체를 관리하는 프로그램(혹은 클래스) 두개가 있는데..

이 두개를 합치는 경우를 생각해 보면

ArrayList와 배열에 대해서 각각 참조하고 있는 객체의 리스트를 얻기 위해서는

각 클래스에 맞는 형식으로 순환문을 돌려야 합니다.

ArrayList라면..

for(int i = 0; i < list.size(); i++) {
...
list.get(i);
...
}

배열이라면
for(int i = 0; i < 배열.length; i++) {
 ...
 배열[i];
...
}


이런식이 되겠죠..

이것을 Itreration을 사용하면 일괄적인 소스로 관리가 가능합니다.

그리고 또 하나는 컬렉션 객체를 숨길 수가 있습니다.

위의 예제 같은 경우에는 마음대로 list나 배열을 만질 수가 있습니다.

add도 할 수 있고 move도 할 수 있고요..

하지만 Iteration을 만들어 그것을 넘겨주게 되면

사용하는 측에서도 Iteration에 대한 사용만 고려하면 되고

객체를 숨겨서 직접 접근하지 못 하도록 할 수도 있습니다.

간단한 예제를 보겠습니다.

일단 관리하고 싶은 클래스입니다.

public class Character {

 private String name;
 private String age;
 private String sex;
 
 public Character(String name, String age, String sex) {
  this.name = name;
  this.age = age;
  this.sex = sex;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getAge() {
  return age;
 }
 public void setAge(String age) {
  this.age = age;
 }
 public String getSex() {
  return sex;
 }
 public void setSex(String sex) {
  this.sex = sex;
 }
}




그리고 위 클래스의 객체를 관리하게 될 클래스들입니다.

public class CharacterArray {
 static final int MAX_VALUES = 6;
 Character[] characters;
 int index = 0;
 
 public CharacterArray() {
  characters = new Character[MAX_VALUES];
  addItem("홍길동","22","남");
  addItem("성춘향","19","여");
  addItem("강감찬","45","남");
  addItem("제다이","?","남");
 }
 
 public void addItem(String name, String age, String sex) {
  if(index >= MAX_VALUES) {
   System.out.println("array is full");
  } else {   
   Character c = new Character(name, age, sex);
   characters[index] = c;
   index++;
  }
 }
 
 public Character[] getCharacters() {
  return characters;
 }
}



 

import java.util.ArrayList;

public class CharacterList {
 ArrayList<Character> characters;
 
 public CharacterList() {
  characters = new ArrayList<Character>();
  addItem("탐크루즈","41","남");
  addItem("안젤리나졸리","33","여");
  addItem("디아나","31","여");
  addItem("러셀크로","41","남");
 }
 
 public void addItem(String name, String age, String sex) {
  Character c = new Character(name, age, sex);
  characters.add(c);
 }
 
 public ArrayList<Character> getCharacters() {
  return characters;
 }
}



두개의 클래스는 각각 내부적으로 ArrayList와 배열을 사용해서 관리하고 있습니다.

여기서 각각의 클래스가 가지고 있는 Character의 객체들을 조회하는 소스를 만들어 보겠습니다.


import java.util.ArrayList;

public class Test {
 public static void main(String[] args) {
  
  CharacterList cl = new CharacterList();
  CharacterArray ca = new CharacterArray();
  
  ArrayList<Character> list = cl.getCharacters();
  Character[] array = ca.getCharacters();

  

  for(int i = 0; i < list.size(); i++) {
   Character c = list.get(i);
   System.out.println(c.getName());
   System.out.println(c.getAge());
   System.out.println(c.getSex());
  }


  System.out.println("############################");

  for(int i = 0; i < array.length; i++) {
   Character c = array[i];
   System.out.println(c.getName());
   System.out.println(c.getAge());
   System.out.println(c.getSex());
  }


 }
}



굵은 글씨로 된 부분을 보시면 직접 배열객체와 ArrayList를 받아오고 있습니다.

그리고 색깔이 다른 부분을 보면 각 캐릭터에 대해서 서로 다른 순환문을 사용하고 있습니다.

그래서 이 반복을 캡슐화 하는 것이 Itrerator 패턴입니다.

Iterator는 java.util에서 인터페이스가 제공됩니다.

그것을 사용해보겠습니다. 그러면 ArrayList에서는 이미 이 Iterator를 구현하고 있기 때문에 CharacterList 클래스는

그 Iterator를 return해주는 부분만 만들어주면 됩니다.

그리고, ArrayList를 리턴해주던 메소드는 삭제합니다.

import java.util.ArrayList;
import java.util.Iterator;

public class CharacterList {
 private ArrayList<Character> characters;
 
 public CharacterList() {
  characters = new ArrayList<Character>();
  addItem("탐크루즈","41","남");
  addItem("안젤리나졸리","33","여");
  addItem("디아나","31","여");
  addItem("러셀크로","41","남");
 }
 
 public void addItem(String name, String age, String sex) {
  Character c = new Character(name, age, sex);
  characters.add(c);
 }
 

 public Iterator getIterator() {
  return characters.iterator();
 }

}


 


하지만 배열은 Iterator가 제공되지 않기 때문에 java.util.Iterator를 구현한 Iterator 클래스를 만들어야 합니다.

간단하게 만들어 보겠습니다.

import java.util.Iterator;

public class CharacterArrayIterator implements Iterator {

 Character[] c;
 int index = 0;
 
 public CharacterArrayIterator(Character[] c) {
  this.c = c;
 }
 
 public boolean hasNext() {
  if(index >= c.length || c[index] == null) {
   return false;
  } else {
   return true;
  }
 }

 public Object next() {
  Character character = c[index];
  index++;
  return character;
 }

 public void remove() {
  throw new UnsupportedOperationException();
 }

}



본래대로라면 remoce() 메서드도 구현을 해줘야 합니다.

하지만 그냥 예외처리 하고 넘어가겠습니다.

그리고 CharacterArray에서 Iterator를 넘겨주는 부분을 구현해야 합니다. 그리고, 배열은 private으로 숨겨야겠죠..

import java.util.Iterator;

public class CharacterArray {
 static final int MAX_VALUES = 4;
 private Character[] characters;
 int index = 0;
 
 public CharacterArray() {
  characters = new Character[MAX_VALUES];
  addItem("홍길동","22","남");
  addItem("성춘향","19","여");
  addItem("강감찬","45","남");
  addItem("제다이","?","남");
 }
 
 public void addItem(String name, String age, String sex) {
  if(index >= MAX_VALUES) {
   System.out.println("array is full");
  } else {   
   Character c = new Character(name, age, sex);
   characters[index] = c;
   index++;
  }
 }
 
 public Iterator getIterator() {
  return new CharacterArrayIterator(characters);
 }

}




그러면 이제 조회를 하는 클래스를 보겠습니다.

import java.util.ArrayList;
import java.util.Iterator;

public class Test {
 public static void main(String[] args) {
  
  CharacterList cl = new CharacterList();
  CharacterArray ca = new CharacterArray();
  
  Iterator i = cl.getIterator();
  Iterator i2 = ca.getIterator();

  
  showList(i);
  showList(i2);
  ArrayList<Character> list = cl.getCharacters();
  Character[] array = ca.getCharacters();
 }
 
 public static void showList(Iterator i) {
  while(i.hasNext()) {
   Character c = (Character)i.next();
   System.out.println(c.getName());
   System.out.println(c.getAge());
   System.out.println(c.getSex());
  }

 }
}



구현체에 따라서 나누어져있던 로직이 Iterator를 사용해서 처리가 되었습니다.

그리고 실제로 이 Test 클래스에서는 자기가 조회하고 있는 객체가

배열인지 ArrayList인지 Map인지 몰라도 상관이 없습니다.

단지 Iterator만 사용 할 뿐입니다.

자바에서 제공되는 API중 Collection 객체들은 이 Iterator를 구현하고 있습니다.


만약에 Character 클래스를 다른 방법으로 관리하는 또다른 클래스가 추가 된다고 하더라도

저 Iterator 인터페이스를 구현한 Iterator 클래스만 만들어주면 됩니다.

컬렉션 객체안에 있는 모든 항목에 접근 하는 방식이 통일 되면, 어떤 종류의 집합체에 대해서도 사용 할 수 있는

코드를 만들 수 있습니다.