본문 바로가기
개발자 꿈나무의 하루

(네이버클라우드 부트캠프) 32일차 - 실습프로젝트(Iterator패턴, 중첩클래스)

by kk_naks 2024. 7. 10.

Iterator 디자인패턴

1.Iterator 패턴의 개념

  • Iterator 디자인 패턴은 컬렉션(예: 리스트, 트리, 해시맵 등)의 내부 구조를 노출하지 않고 그 요소들에 순차적으로 접근할 수 있는 방법을 제공하는 패턴이다.
  • 반복자(Iterator)를 사용하여 컬렉션의 요소를 하나씩 차례대로 탐색할 수 있다.

2.Iterator 설계패턴

1) Iterator의 필요성

  • 자료 구조마다 메서드를 호출하는 방법이 상이하다.
  • 자료 구조마다 동일 기능을 하는 메서드가 다르면 유지보수에 불리하다.
image

2) Iterator 패턴적용하기

  • Iterator 인터페이스를 활용하여 동일한 메서드를 만든다.
  • 객체 안에 실질적인 조회 코드를 숨긴다.
  • 기존의 객체가 직접적인 조회를 하는 것이 아닌 Iterator가 대신하게 한다.
image

3.실습프로젝트에 적용하기

  • 실습프로젝트에서는 List구조에 대해서 적용한다.

1) 기존코드 분석

  • 기존의 Command 구조는 List에서 toArray()를 호출하여 리스트업하는 구조이다.
image
    //BoardCommand
    private void listBoard() {
        System.out.println("번호 제목 작성일 조회수");
        for (Object obj : boardList.toArray()) {
        Board board = (Board) obj;
        System.out.printf("%d %s %tY-%3$tm-%3$td %d\n",
            board.getNo(), board.getTitle(), board.getCreatedDate(), board.getViewCount());
        }
    }

2) Iterator 적용

  • 조회코드를 캡슐화하고 사용규칙을 통일한다 => 인터페이스를 생성한다.
  • 생성한 인터페이스를 구현하여 ListIterator을 생성한다.
  • ListIterator에 기존 List 배열을 받아서 조회를 한다.
image
코드 접기/펴기
    // Iterator 인터페이스 생성
    public interface Iterator {
    boolean hasNext();
    Object next();
    }

    // ListIterator 구현체 생성
    public class ListIterator implements Iterator {
    private List list;
    private int cursor;

    public ListIterator(List list) {
        this.list = list;
    }

    @Override
    public boolean hasNext() {
        return cursor < list.size();
    }

    @Override
    public Object next() {
        return list.get(cursor++);
    }
    }
    // list 인터페이스 변경
    public interface List {
    void add(Object obj);

    Object remove(int index);

    Object[] toArray();

    int indexOf(Object obj);

    int size();

    Object get(int index);

    Iterator iterator();
    }
    // AbstractList 변경
    public abstract class AbstractList implements List {
    protected int size = 0;

    @Override
    public int size() {
        return size;
    }

    @Override
    public Iterator iterator() {
        return new ListIterator(this);
    }
    }
    // BoardCommand 메서드변경
    private void listBoard() {
        System.out.println("번호 제목 작성일 조회수");
        Iterator iterator = boardList.iterator();
        while (iterator.hasNext()) {
        Board board = (Board) iterator.next();
        System.out.printf("%d %s %tY-%3$tm-%3$td %d\n", board.getNo(), board.getTitle(),
            board.getCreatedDate(), board.getViewCount());
        }
    }

4.Enhanced for문

for (변수타입 변수명 : 배열 or Iterable 구현체) {}
  • 진보된 for문에는 배열 또는 Iterable이 가능하다.
  • ArrayList와 같은 데이터구조는 Iterable의 인터페이스를 사용한 구현체이므로 가능하다.
  • 직접적으로 Iterable의 구현체를 생성하여도 가능하다.

중첩클래스

1.중첩클래스의 개념

  • 클래스 내부에 선언한 클래스이다.
  • 특정클래스와만 관계를 맺을 경우 중첩클래스로 선언하는 것이 유지보수에 유리하다.
  • 외부에는 중첩관계를 감춤으로써 코드의 복잡성을 줄일 수 있다.

2.중첩클래스의 종류

  • 중첩클래스는 크게 클래스의 멤버로서 선언되는 멤버클래스와 메서드내부에 선언 되는 중첩클래스로 나뉜다.
  • 멤버클래스는 다시 인스턴스 멤버 클래스와 정적멤버클래스로 나뉜다.
image
코드 접기/펴기
//중첩 클래스 예시 
public class LinkedList extends AbstractList {

  private Node first;
  private Node last;
  int size;

    /* 내부 코드 생략*/ 

  // 중첩클래스 삽입
  public static class Node {

    Object value;
    Node next;

    public Node(Object value) {
      this.value = value;
    }
  }
}

1) 정적멤버 클래스

  • static 키워드와 함께 선언된 클래스를 말한다.
  • 바깥 클래스를 생성하지 않아도 내부 클래스를 생성할 수 있다.
[public] class A{
    [public | private] static class B{
        //정적멤버클래스 
    }
}

ListIterator에 적용 해보기

코드 접기/펴기
public abstract class AbstractList implements List {
  protected int size = 0;

  public int size() { return size;}

  public Iterator iterator() {
    //중첩클래스의 생성자에 넘겨줄 인스턴스가 필요하다.
    return new ListIterator(this);
  }

  static public class ListIterator implements Iterator {
    // AbstractList의 중첩이지만 내부적으로
    // List타입의 주소를 넘겨줘야한다.
    private List list;
    private int cursor;

    // private List와 바깥 List를 동기화하기 위해
    // 생성자가 필요하다.
    public ListIterator(List list) {
      this.list = list;
    }

    @Override
    public boolean hasNext() {
      return cursor < list.size();
    }

    @Override
    public Object next() {
      return list.get(cursor++);
    }
  }
}

2) 인스턴스멤버 클래스

  • 인스턴스 멤버클래스는 바깥클래스에서 자유롭게 사용가능하다.
  • 바깥클래스를 생성해야만 내부 클래스를 생성할 수 있다.
[public] class A{
    [public | private] class B{
        //인스턴스멤버클래스 
    }
}

ListIterator에 적용 해보기

코드 접기/펴기
public abstract class AbstractList implements List {
  protected int size = 0;

  public int size() { return size;}
  // 컴파일러: 바깥 클래스의 인스턴스 주소를 전달하는 코드로 자동 변환
  public Iterator iterator() {
    return new ListIterator();
  }

  public class ListIterator implements Iterator {
    private int cursor;

    @Override
    public boolean hasNext() {
      return cursor < AbstractList.this.size();
    }


    // 바깥 클래스의 인스턴스를 사용하려면
    // => 바깥클래스명.this 라고 지정해야 한다.
    // => 중첩 클래스 안에 해당 필드나 메서드가 없다면
    //    바깥클래스명.this 생략 가능
    @Override
    public Object next() {
      return get(cursor++);
    }
  }
}

3) 로컬 클래스

  • 생성자 또는 메서드 내부에서 선언된 클래스를 말한다.
  • 메서드가 호출되어 있는 동안에만 내부클래스를 생성할 수 있다.
[public] class A{
    public A(){
        class B1{ }
    }

    public void method(){
        class B2{ }
    }
}

ListIterator에 적용 해보기

코드 접기/펴기
public abstract class AbstractList implements List {
  protected int size = 0;

  public int size() { return size;}

  public Iterator iterator() {
    public class ListIterator implements Iterator {
    private int cursor;

    @Override
    public boolean hasNext() {
      return cursor < AbstractList.this.size();
    }

    @Override
    public Object next() {
      return get(cursor++);
    }
  }
    return new ListIterator();
  }
}

추가) 익명구현객체

  • 익명객체는 이름이 없는 객체를 말한다.
  • 명시적으로 클래스를 선언하지 않기 때문에 쉽게 객체를 생성 할 수 있다.
  • 주로 필득밧, 로컬변수값, 매개변수값으로 사용한다.
new 부모생성자(매개값,...){
    //필드
    //메서드
}

ListIterator에 적용 해보기

코드 접기/펴기
public abstract class AbstractList implements List {
  protected int size = 0;

  public int size() { return size;}

  public Iterator iterator() {
    return new Iterator(){
        private int cursor;

        @Override
        public boolean hasNext() {
        return cursor < AbstractList.this.size();
        }

        @Override
        public Object next() {
        return get(cursor++);
        }
    };
  }
}