[디자인 패턴] Iterator 패턴

3Beom's 개발 블로그·2023년 4월 4일
0
post-custom-banner

Iterator 패턴

1. Iterator 패턴

<용어 정의>

  • 집합체 : 특정 객체들을 자료구조에 모아두고 로직을 처리하는 객체
  • 항목 : 집합체 내 자료구조에 저장되는 객체
    (여러명의 학생들이 A수업을 수강할 때, A수업 객체가 집합체, 학생 객체가 항목에 해당한다.)

1-1. Iterator 패턴

  • 특정 집합체(혹은 컬렉션)의 구현 방법을 노출시키지 않으면서 해당 집합체 내 모든 항목에 접근할 수 있도록 설계하는 패턴이다.
  • 집합체 내에서 구현되는 기능 혹은 동작 방식을 몰라도 내부 항목들에 접근하여 반복 작업을 수행할 수 있다.

집합체 본연의 기능순회 기능을 분리하는 것 → SRP(단일 책임 원칙)

해당 집합체의 사용자(클라이언트)에게 집합체 구현 내용을 노출시키지 않아도 잘 동작될 수 있도록 설계하는 것 → 캡슐화

이로써 사용자는 집합체 내부에서 항목들이 어떤 자료구조(Array, List, Set, …)로 구현되어 있는지 몰라도 각 항목에 접근하여 특정 동작을 수행할 수 있다.

1-2. Iterator 패턴 구조

사진1. Iterator 패턴 구조

  • 일반적으로 Iterator 패턴은 사진1과 같은 구조를 갖는다.
  • 각 요소는 다음과 같다.
    • Client : 집합체 객체와 Iterator 객체를 사용하는 사용자(객체가 사용되는 클래스 혹은 개발자)
    • Interface
      • Iterator : 집합체 내 항목들에 대한 반복 작업을 수행할 수 있도록 설계된 인터페이스.
      • Aggregate : 집합체 인터페이스. Iterator를 생성 및 반환하는 메서드(createIterator())를 포함하고 있다.
    • Class
      • ConcreteIterator : Iterator 인터페이스의 구현체 클래스. ConcreteAggregate에 적합한 형태로 메서드가 재정의된다.
      • ConcreteAggregate : Aggregate 인터페이스의 구현체. ConcreateIterator 객체를 생성 및 반환하도록 createIterator() 메서드를 재정의한다.

2. 구현 예

2-1. 예시 설정

  • 여러명의 학생들이 특정 수업을 수강하는 경우로 예시를 설정한다.
  • 우선 Iterator 패턴과 무관하게 다음의 클래스가 필요하다.
    • 학생 클래스 : Student
    • 수업 클래스 : Lecture
  • 여기서 수업 클래스가 집합체, 학생 클래스가 항목에 해당한다고 볼 수 있다.
  • 각각은 다음과 같이 간단히 구현될 수 있다.
public class Student {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

}
public class Lecture{
    private Student[] students;
    private int numOfStudents;

    public Lecture(int maxOfStudents) {
        this.students = new Student[maxOfStudents];
    }

    public Student getStudent(int index) {
        return students[index];
    }

    public void addStudent(Student student) {
        students[numOfStudents] = student;
        numOfStudents += 1;
    }

    public int getNumOfStudents() {
        return numOfStudents;
    }
}

2-2. Iterator 패턴 적용

  • 사진1에서 다룬 구조를 그대로 적용해 보면, 다음과 같은 인터페이스가 필요하다.

    • Iterator
    • Aggregate
  • 먼저 Iterator 인터페이스와 구현체를 구현해보자.

  • Iterator 인터페이스는 다음과 같이 구현될 수 있다.

public interface Iterator {
    boolean hasNext();
    Object next();
}
  • 본 인터페이스를 Lecture 클래스에 맞추어 구현하면 다음과 같이 구현될 수 있다. : LectureIterator
public class LectureIterator implements Iterator {

    private Lecture lecture;
    private int index;

    public LectureIterator(Lecture lecture) {
        this.lecture = lecture;
        this.index = 0;
    }

    @Override
    public boolean hasNext() {
        if (index < lecture.getNumOfStudents())
            return true;
        return false;
    }

    @Override
    public Object next() {
        return lecture.getStudent(index++);
    }
}
  • 다음은 Iterator를 생성 및 반환하는 Aggregate 인터페이스를 구현한다.
public interface Aggregate {
    Iterator createIterator();
}
  • 이를 Lecture 클래스에서 구현하면 다음과 같이 구현될 수 있다.
public class Lecture implements Aggregate {
    private Student[] students;
    private int numOfStudents;

    public Lecture(int maxOfStudents) {
        this.students = new Student[maxOfStudents];
    }

    public Student getStudent(int index) {
        return students[index];
    }

    public void addStudent(Student student) {
        students[numOfStudents] = student;
        numOfStudents += 1;
    }

    public int getNumOfStudents() {
        return numOfStudents;
    }

    @Override
    public Iterator createIterator() {
        return new LectureIterator(this);
    }
}
  • 이렇게 구현된 인터페이스와 구현체들은 사용자에 의해 다음과 같이 활용될 수 있다.
Lecture lecture = new Lecture(5);

lecture.addStudent(new Student("student1"));
lecture.addStudent(new Student("student2"));
lecture.addStudent(new Student("student3"));
lecture.addStudent(new Student("student4"));
lecture.addStudent(new Student("student5"));

Iterator lectureIterator = lecture.createIterator();

Student student;
while (lectureIterator.hasNext()) {
    student = (Student)lectureIterator.next();
    System.out.println(student.getName());
}
//student1 
//student2
//student3
//student4
//student5
  • 사용자(Client)는 집합체(Lecture) 내에서 항목(Student)들이 어떤식으로 저장되어 있는지 모르지만, Iterator를 통해 각 항목들에 접근하여 특정 동작을 수행할 수 있다.

3. 장단점

  • 장점

    • 사용자(Client)는 집합체가 어떻게 구현되어 있는지 알 필요도 없고 알 수도 없다. 하지만 각 항목들을 순회하는 동작을 수행할 수 있다.

      → 캡슐화

    • 순회 관련 로직을 Iterator로 분리함으로써 집합체는 이를 다루지 않아도 된다. 즉, 집합체 본연의 로직과 순회 관련 로직을 분리한 것이다.

      → SRP

    • Iterator 인터페이스를 둠으로써 여러 형태의 집합체에 적용할 수 있다. → 다형성

  • 단점

    • 개발 과정이 복잡해 질 수 있다.
      • 간단한 순회 로직의 경우, Iterator 패턴이 불필요 할 수 있다.
    • 항목을 저장하고 있는 컬렉션의 순회 메서드를 직접 활용하는 것이 더 효율적일 수 있다.

참고 자료

profile
경험과 기록으로 성장하기
post-custom-banner

0개의 댓글