Posts 학원 #38일차: 익명 클래스, 커맨드 디자인 패턴, 상수 다루기
Post
Cancel

학원 #38일차: 익명 클래스, 커맨드 디자인 패턴, 상수 다루기

anonymous class

로컬 클래스 vs 익명 클래스

로컬 클래스로 인터페이스 구현하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Exam0120 {
  interface A {
    void print();
  }
  
  public static void main(final String[] args) {
    class My implements A {
      @Override
      public void print() {
        System.out.println("Hello!");
      }
    }
    
    A obj = new My();
    obj.print();
  }
}

로컬클래스를 익명 클래스로 바꾸는 과정

  • 1) 로컬 클래스로 선언한다.
    • class My implements A{}
  • 2) 클래스의 이름을 지운다.
    • class implements A{}
    • 클래스의 이름이 없기 때문에 인스턴스를 생성할 수 없다.
    • 클래스의 이름이 없기 때문에 ‘class’, ‘implements’키워드를 붙일 수 없다.
  • 3) class, implements 키워드를 지운다.
    • A {};
  • 4) 인터페이스 이름 바로 앞에 ‘new’ 키워드를 둔다.
    • new A {}
    • 클래스의 이름이 없기 때문에 따로 인스턴스를 생성할 수 없어 바로 생성한다.
  • 5) 수퍼 클래스의 생성자를 호출한다.
    • new A(){};
    • 익명 클래스의 생성자가 없기 때문에 수퍼 클래스의 생성자를 호출해야 한다.
    • 객체 생성할 때 항상 생성자를 호출해야 하는데, 클래스에 이름이 없으면 생성자를 만들 수 없다. 따라서 호출할 익명 클래스의 생성자가 없다. 그래서 수퍼 클래스의 생성자를 호출해야 한다.
    • 자바의 모든 클래스는 따로 수퍼 클래스를 지정하지 않으면 java.lang.Object 클래스의 생성자를 호출하도록 지정해야 한다. Object 의 클래스의 생성자는 기본 생성자 하나 뿐이다.
    • 인터페이스 이름 뒤에 기본 생성자를 호출하는 괄호를 추가한다.
  • 6) 익명 클래스의 레퍼런스를 인터페이스나 수퍼클래스로 선언한다.
    • A obj = new A() {}
    • 익명 클래스는 이름이 없기 때문에 익명 클래스로 레퍼런스를 선언할 수없다.
    • 그래서 레퍼런스는 익명 클래스가 구현하는 인터페이스나 익명 클래스가 상속 받는 수퍼 클래스로 선언해야 한다.

익명 클래스로 인터페이스를 구현하기

  • 문법: 인터페이스명 레퍼런스 = new 인터페이스명(){}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Exam0120 {
  // 인터페이스의 경우 static으로 선언하지 않아도 스태틱 멤버에서 사용할 수 있다.
  interface A {
    void print();
  }
  
  public static void main(final String[] args) {
    A obj = new A() {
      @Override
      public void print() {
        System.out.println("Hello!");
      }
    };
    
    obj.print();
  }
}

클래스 상속

클래스를 상속 받는 익명 클래스 만들기

  • 문법: 클래스명 레퍼런스 = new 클래스명(생성자 파라미터, ...) {};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Exam0210 {
  // 클래스는 스태틱으로 선언해야만 스태틱 멤버에서 접근할 수 있다.
  static abstract class A {
    public abstract void print();
  }
  
  public static void main(String[] args) {
    A obj = new A() {
      @Override
      public void print() {
        System.out.println("Hello2!");
      }
    };
    obj.print();
  }
}
  • 익명 클래스를 정의할 때 호출할 수퍼 클래스의 생성자를 지정할 수 있다.
    • 생성자에 넘겨주는 파라미터로 호출될 생성자를 지정한다.
  • 수퍼 클래스의 메서드 오버라이딩하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Exam0310 {
  static class A {
    String name;
    
    public A() {
      name = "이름 없음";
    }
    
    public A(final String name) {
      this.name = name;
    }
    
    public void print() {
      System.out.println(name);
    }
  }
  
  public static void main(final String[] args) {
    final A obj = new A() {
      // 수퍼클래스의 메서드를 오버라이딩한다.
      @Override
      public void print() {
        System.out.println("%s님 반갑습니다!", name);
      }
    }
    obj.print(); //이름없음님 반갑습니다!

    // 익명 클래스의 인스턴스를 만들 때 값을 지정하면 그 타입의 값을 받는 생성자 호출
    final A obj2 = new A("홍길동") {
      @Override
      public void print() {
        System.out.println("%s님 안녕하세요", name);
      }
    }
    obj2.print(); //홍길동님 안녕하세요
  }
}

익명 클래스가 놓이는 장소

스태틱 필드, 인스턴스 필드, 로컬 변수, 파라미터의 값을 준비할 때 익명 클래스를 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class Exam0410 {
  interface A {
    void print();
  }
  
  // 스태틱 필드
  static A obj = new A() {
    @Override
    public void print() {
      System.out.println("Hello1!");
    }
  };
  
  // 인스턴스 필드
  static A obj2 = new A() {
    @Override
    public void print() {
      System.out.println("안녕!");
    }
  };
  
  // 로컬 변수
  void m1() {
    A obj3 = new A() {
      @Override
      public void print() {
        System.out.println("Bunos dias!");
      }
    };
  }

  void m2(A obj) {
    obj.print();
  }
  
  public static void main(String[] args) {
    Exam0430 r = new Exam0430();
    r.m1();
  
    // 파라미터
    r.m2(new A() {
      @Override
      public void print() {
        System.out.println("Como estas?");
      }
    });
  }
}

고정값 다루기

1단계: 카테고리를 정수값으로 받기

제품의 카테고리를 정수 값으로 설정하고 값을 입력할 때 사용한다. 숫자이기에 코드를 보는 것만으로 어떤 카테고리인지 알 수 없다. 따라서 보통 코드에 주석으로 붙여 추가적인 정보를 제공한다.

1
2
3
4
5
6
7
8
package com.eomcs.oop.ex11.g.step1;

public class Product {
  int category;
  String name;
  String maker;
  int price;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.eomcs.oop.ex11.g.step1;

public class Exam0110 {
  public static void main(String[] args) {
    Product p = new Product();
    p.category = 10; // 가전 / TV
    p.name = "울트라비전 뷰";
    p.price = 2000000;

    Product p2 = new Product();
    p2.category = 3; // 컴퓨터 / RAM
    p2.name = "삼성모듈램 4GB";
    p2.price = 80000;

    Product p3 = new Product();
    p3.category = 101; // 책 / 소설
    p3.name = "토지";
    p3.price = 18000;
  }
}

2단계: 카테고리를 문자열로 받기

1단계에서는 카테고리를 숫자로 받기 때문에 해당 카테고리가 무엇인지 알 수 없었다. 제품의 카테고리 값을 문자열로 받으면 따로 주석을 달 필요가 없어서 좋다. 단 문자열의 오타가 들어갈 경우가 생기면 프로그램이 의도대로 동작하지 않을 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.eomcs.oop.ex11.g.step2;

public class Exam0110 {
  public static void main(String[] args) {
    Product p = new Product();
    p.category = "appliance/tv";
    p.name = "울트라비전 뷰";
    p.price = 2000000;

    Product p2 = new Product();
    p2.category = "computer/ram";
    p2.name = "삼성모듈램 4GB";
    p2.price = 80000;

    Product p3 = new Product();
    p3.category = "book/novul"; // <== book/novel 이름을 잘못 입력한 경우.
    p3.name = "토지";
    p3.price = 18000;
  }
}

3단계: 카테고리 문자열을 상수로 정의

상수를 정의하면 잘못된 문자열을 입력할 가능성이 없다.

제품의 값처럼 고정된 값은 상수로 정의해 놓고 쓰면 코드를 작성할 때 오타를 줄일 수 있고, 코드를 읽기 쉬워진다.

1
2
3
4
5
6
7
package com.eomcs.oop.ex11.g.step3;

public class Product {
  public static final String appliance_tv = "appliance/tv";
  public static final String computer_ram = "computer/ram";
  public static final String book_novel = "book/novel";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 상수를 활용한 후 : 카테고리를 문자열을 상수로 정의
package com.eomcs.oop.ex11.g.step3;

public class Exam0110 {
  public static void main(String[] args) {
    Product p = new Product();
    p.category = Product.appliance_tv;
    p.name = "울트라비전 뷰";
    p.price = 2000000;

    Product p2 = new Product();
    p2.category = Product.computer_ram;
    p2.name = "삼성모듈램 4GB";
    p2.price = 80000;

    Product p3 = new Product();
    p3.category = Product.book_novel; // <=== 잘못된 값을 넣을 수 없다!
    p3.name = "토지";
    p3.price = 18000;
  }
}

4단계: 카테고리를 정수 타입의 상수로 정의

상수를 문자열로 정의하면 값을 저장할 때 메모리를 많이 차지하기 때문에 실무에서는 보통 정수 값으로 처리한다. 이때 상수의 이름을 대문자로 지어서 상수임을 쉽게 알 수 있도록 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.eomcs.oop.ex11.g.step4;

public class Product {
  public static final int COMPUTER_CPU = 1;
  public static final int COMPUTER_VGA = 2;
  public static final int COMPUTER_RAM = 3;
  public static final int COMPUTER_MOUSE = 4;
  public static final int COMPUTER_KEYBOARD = 5;

  public static final int APPLIANCE_TV = 10;
  public static final int APPLIANCE_AUDIO = 11;
  public static final int APPLIANCE_DVD = 12;
  public static final int APPLIANCE_VACUUMCLEANER = 13;

  public static final int BOOK_POET = 100;
  public static final int BOOK_NOVEL = 101;
  public static final int BOOK_ESSAY = 102;
  public static final int BOOK_IT = 103;
  public static final int BOOK_LANG = 104;

  int category;
  String name;
  String maker;
  int price;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 상수를 활용한 후 : 카테고리를 정수타입의 상수로 정의
package com.eomcs.oop.ex11.g.step4;

public class Exam0110 {
  public static void main(String[] args) {
    
    Product p = new Product();
    p.category = Product.APPLIANCE_TV;
    p.name = "울트라비전 뷰";
    p.price = 2000000;

    Product p2 = new Product();
    p2.category = Product.COMPUTER_RAM;
    p2.name = "삼성모듈램 4GB";
    p2.price = 80000;

    Product p3 = new Product();
    p3.category = Product.BOOK_NOVEL;
    p3.name = "토지";
    p3.price = 18000;

  }
}

5단계: 상수를 별도의 클래스로 분리하여 다루기

카테고리 사수가 많을 경우 별도의 클래스로 분리하는 것이 상수 관리에 좋다. Product 클래스에 정의되어 있던 상수를 Category 클래스로 분리한다.

1
2
3
4
5
6
7
8
package com.eomcs.oop.ex11.g.step5;

public class Product {
  int category;
  String name;
  String maker;
  int price;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.eomcs.oop.ex11.g.step5;

public class Category {
  public static final int COMPUTER_CPU = 1;
  public static final int COMPUTER_VGA = 2;
  public static final int COMPUTER_RAM = 3;
  public static final int COMPUTER_MOUSE = 4;
  public static final int COMPUTER_KEYBOARD = 5;

  public static final int APPLIANCE_TV = 10;
  public static final int APPLIANCE_AUDIO = 11;
  public static final int APPLIANCE_DVD = 12;
  public static final int APPLIANCE_VACUUMCLEANER = 13;

  public static final int BOOK_POET = 100;
  public static final int BOOK_NOVEL = 101;
  public static final int BOOK_ESSAY = 102;
  public static final int BOOK_IT = 103;
  public static final int BOOK_LANG = 104;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 상수를 별도의 클래스로 분리하여 다루기
package com.eomcs.oop.ex11.g.step5;

public class Exam0110 {
  public static void main(String[] args) {
    Product p = new Product();
    p.category = Category.APPLIANCE_TV;
    p.name = "울트라비전 뷰";
    p.price = 2000000;

    Product p2 = new Product();
    p2.category = Category.COMPUTER_RAM;
    p2.name = "삼성모듈램 4GB";
    p2.price = 80000;

    Product p3 = new Product();
    p3.category = Category.BOOK_NOVEL;
    p3.name = "토지";
    p3.price = 18000;
  }
}

다음과 같이 static 멤버를 import 해서 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 상수를 별도의 클래스로 분리하여 다루기 - static 멤버 import
package com.eomcs.oop.ex11.g.step5;

import static com.eomcs.oop.ex11.g.step5.Category.APPLIANCE_TV;
import static com.eomcs.oop.ex11.g.step5.Category.BOOK_NOVEL;
import static com.eomcs.oop.ex11.g.step5.Category.COMPUTER_RAM;

// 또는 다음과 같이 wildcard를 사용해도 된다.
// import static com.eomcs.oop.ex11.c.step5.Category.*;

public class Exam0120 {
  public static void main(String[] args) {
    Product p = new Product();
    p.category = APPLIANCE_TV;
    p.name = "울트라비전 뷰";
    p.price = 2000000;

    Product p2 = new Product();
    p2.category = COMPUTER_RAM;
    p2.name = "삼성모듈램 4GB";
    p2.price = 80000;

    Product p3 = new Product();
    p3.category = BOOK_NOVEL;
    p3.name = "토지";
    p3.price = 18000;
  }
}

6단계: 상수를 그룹 별로 별도의 클래스로 분리하여 다루기

각 카테고리 별로 클래스를 분리하면 관리하기가 쉬워진다.

1
2
3
4
5
6
7
public class Computer {
  public static final int CPU = 1;
  public static final int VGA = 2;
  public static final int RAM = 3;
  public static final int MOUSE = 4;
  public static final int KEYBOARD = 5;
}
1
2
3
4
5
6
public class Appliance {
  public static final int TV = 10;
  public static final int AUDIO = 11;
  public static final int DVD = 12;
  public static final int VACUUMCLEANER = 13;
}
1
2
3
4
5
6
7
public class Book {
  public static final int POET = 100;
  public static final int NOVEL = 101;
  public static final int ESSAY = 102;
  public static final int IT = 103;
  public static final int LANG = 104;
}
1
2
3
4
5
6
public class Product {
  int category;
  String name;
  String maker;
  int price;
}

각 카테고리 그룹을 별도의 클래스로 분리하기 전에는 상수 이름에 카테고리가 포함되었다. 그런데 카테고리 그룹 별로 클래스를 분리하면 다음과 같이 간결해진다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 상수를 그룹 별로 별도의 클래스로 분리하여 다루기
import static com.eomcs.oop.ex11.g.step6.Appliance.TV;
import static com.eomcs.oop.ex11.g.step6.Book.NOVEL;
import static com.eomcs.oop.ex11.g.step6.Computer.RAM;

public class Exam0110 {
  public static void main(String[] args) {
  
    Product p = new Product();
    p.category = TV;
    p.name = "울트라비전 뷰";
    p.price = 2000000;

    Product p2 = new Product();
    p2.category = RAM;
    p2.name = "삼성모듈램 4GB";
    p2.price = 80000;

    Product p3 = new Product();
    p3.category = NOVEL;
    p3.name = "토지";
    p3.price = 18000;
  }
}

각각의 상수를 별도의 클래스로 묶어 관리하는 것은 좋으나, 자잘한 클래스들을 별도의 파일로 분리하여 패키지 멤버 클래스로 정의하니까 클래스가 여러 개 생겨서 관리하기가 번거롭다.

7단계: 상수 코드를 스태틱 중첩 클래스로 다루기

각 카테고리에 대한 클래스를 보면 상수를 묶어서 관리하는 용도로 만들어졌고, 클래스의 크기도 작다. 이런 클래스를 별도의 파일로 분리하면 여러 개의 파일이 생겨서 오히려 관리하기가 번거롭다. 즉 자잘한 클래스들이 여러 파일에 분산되기 때문에 관리하기 번거로워진다. 이때 중첩 클래스로 만들면 사용할 때 계층적으로 작성하기 때문에 이해하기가 쉬워진다. int value = Category.computer.CPU;와 같이 사용할 수 있다. 이렇게 바깥 클래스를 패키지처럼 생각할 수 있어 이해하고 관리하기 편하다. 이때 중첩 클래스를 소문자로 지어서 마치 일반 필드인 것처럼 보이게 하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// static nested class 문법을 이용하여 상수를 효과적으로 관리하기

public class Category {

  public static class computer {
    public static final int CPU = 1;
    public static final int VGA = 2;
    public static final int RAM = 3;
    public static final int MOUSE = 4;
    public static final int KEYBOARD = 5;
  }

  public static class appliance {
    public static final int TV = 10;
    public static final int AUDIO = 11;
    public static final int DVD = 12;
    public static final int VACUUMCLEANER = 13;
  }

  public static class book {
    public static final int POET = 100;
    public static final int NOVEL = 101;
    public static final int ESSAY = 102;
    public static final int IT = 103;
    public static final int LANG = 104;
  }
}

카테고리 값을 지정할 때 OGNL 표기법으로 지정한다. OGNL(Object Graph Navigation Language)란 자바에서 객체 안에 있는 필드를 가리킬 때 점(.)을 이용하여 표기하는 방법을 말한다. 객체 안에 객체가 계층적으로 들어 있을 때 파일 경로를 가리키듯 점(.)을 이용하여 가리킬 수 있다. (예: 객체.필드.필드.필드 = 100)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 상수 코드를 스태틱 중첩 클래스로 다루기
package com.eomcs.oop.ex11.g.step7;

public class Exam0110 {
  public static void main(String[] args) {
    Product p = new Product();
    p.category = Category.appliance.TV;
    p.name = "울트라비전 뷰";
    p.price = 2000000;

    Product p2 = new Product();
    p2.category = Category.computer.RAM;
    p2.name = "삼성모듈램 4GB";
    p2.price = 80000;

    Product p3 = new Product();
    p3.category = Category.book.NOVEL;
    p3.name = "토지";
    p3.price = 18000;
  }
}

mini pms 프로젝트 수행

v.26-d: anonymous class

익명 클래스

  • 이름이 없는 클래스
  • 이름이 없기 때문에 클래스를 정의하자마자 바로 인스턴스를 생성해야 한다.
  • 일반적으로 인스턴스를 딱 한 개만 만들 때 익명 클래스로 정의한다.

실습

1단계: ListIterator 구현체를 iterator() 메서드에서 익명 클래스로 정의한다.

익명 클래스는 이름이 없기 때문에 클래스를 정의하자마자 바로 인스턴스를 생성해야 한다. 익명 클래스에서도 로컬 클래스와 마찬가지로 바깥 클래스의 인스턴스 주소를 사용하고 싶다면 바깥 클래스의 이름으로 사용한다. (AbstractList.this.size()) 이때 바깥 클래스의 인스턴스를 가리키는 AbstractList.this는 생략할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.eomcs.util;

import java.util.NoSuchElementException;

public abstract class AbstractList<E> implements List<E>{

	//...
  @Override
  public Iterator<E> iterator() {

    // 익명 클래스는 이름이 없기 때문에 
    // 클래스를 정의하자마자 바로 인스턴스를 생성해야 한다.
    return new Iterator<E>() {
      int cursor;

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

      @Override
      public E next() {
        if (cursor ==  /*AbstractList.this.*/size())
          throw new NoSuchElementException();
        return get(cursor++);
      }
    };
  }
}

2단계: StackIterator 구현체를 iterator() 메서드에서 익명 클래스로 정의한다.

StackIterator는 ListIterator와 달리 생성자를 가지고 있었다. 익명 클래스는 이름이 없기 때문에 생성자를 만들 수 없는데(익명 클래스의 생성자는 내부적으로만 존재하고 개발자가 직접 만들 수 없다.), 어떻게 해야 할까? 이런 경우에 인스턴스 초기화 블록을 사용할 수 있다. 인스턴스 블록과 인스턴스 필드의 초기화 문장은 모두 생성자 안으로 자동으로 들어가기 때문에 인스턴스 블록에 생성자의 내용을 담아주면 내부적으로 숨겨져 있는 생성자 몸체 안으로 들어간다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.eomcs.util;

import java.util.EmptyStackException;
import java.util.NoSuchElementException;

public class Stack<E> extends LinkedList<E> {

	//...
  @Override
  public Iterator<E> iterator() {
    // AbstractList에서 구현한 iterator()를 Stack 자료 구조에 맞춰 오버라이딩
    return new Iterator<E>() {
      Stack<E> stack;

      { // 인스턴스 초기화 블록
        try {
          this.stack = Stack.this.clone();
        } catch (Exception e) {
          throw new RuntimeException("큐를 복제하는 중에 오류 발생!");
        }
      }

      @Override
      public boolean hasNext() {
        return !stack.empty();
      }

      @Override
      public E next() {
        if (stack.empty())
          throw new NoSuchElementException();
        return stack.pop();
      }
    };
  }
}

3단계: QueueIterator 구현체를 iterator() 메서드에서 익명 클래스로 정의한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.eomcs.util;

import java.util.NoSuchElementException;

public class Queue<E> extends LinkedList<E> {

  public boolean offer(E e) {
    return add(e);
  }

  public E poll() {
    if (size() == 0) {
      return null;
    }
    return remove(0);
  }

  public E peek() {
    if (size() == 0) {
      return null;
    }
    return get(0);
  }

  @SuppressWarnings("unchecked")
  @Override
  public Queue<E> clone() throws CloneNotSupportedException {
    Queue<E> newQueue = new Queue<>();
    Object[] values = this.toArray();
    for (Object value : values) {
      newQueue.offer((E) value);
    }
    return newQueue;
  }

  @Override
  public Iterator<E> iterator() {
    return new Iterator<E>() {
      Queue<E> queue;

      { // 인스턴스 초기화 블록
        try {
          this.queue = Queue.this.clone();
        } catch (Exception e) {
          throw new RuntimeException("큐를 복제하는 중에 오류 발생!");
        }
      }

      @Override
      public boolean hasNext() {
        return queue.size() > 0;
      }

      @Override
      public E next() {
        if (queue.size() == 0)
          throw new NoSuchElementException();
        return queue.poll();
      }
    };
  }
}

v.27: 자바 컬렉션 API 사용하기

자바 컬렉션 API

컬렉션(collection)은 여러 개의 항목을 담는 컨테이너(container) 객체이다. 즉 데이터를 저장하고, 꺼내고, 삭제하는 등의 데이터 목록을 다룰 때 사용하는 객체이다. 공식적으로는 컬렉션 프레임워크(collection framework)라고 부른다.

java.util 패키지

컬렉션과 관련된 인터페이스와 클래스들이 들어 있다. 자료구조, 탐색, 정렬 등 알고리즘에 대한 객체 사용 규칙을 인터페이스로 정의하고 있다. 또한 그 인터페이스의 구현체를 제공한다.

실습

자바에서 Stack 클래스는 Vector를 상속받는다. 즉 vector를 stack처럼 사용할 수 있게 해준다. push(), pop(), peek(), empty(), 연산자는 제공되지만, iterator() 메서드의 경우 Vector 클래스에서 상속받은 것이기 때문에 스택의 본래 기능(Last In First Out)을 수행하지 못하고, 넣은 순서대로 값을 조회하는 객체를 리턴한다. 자바 공식 문서에서도 Stack 클래스보다는 더 완전하고 일관된 LIFO 스택 연산자의 묶음인 Dequeue 인터페이스와 그 구현체를 사용하라고 권장하고 있다.

A more complete and consistent set of LIFO stack operations is provided by the Deque interface and its implementations, which should be used in preference to this class. For example:

Deque<Integer> stack = new ArrayDeque<Integer>();

출처: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Stack.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package com.eomcs.pms;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import com.eomcs.pms.domain.Board;
import com.eomcs.pms.domain.Member;
import com.eomcs.pms.domain.Project;
import com.eomcs.pms.domain.Task;
import com.eomcs.pms.handler.BoardHandler;
import com.eomcs.pms.handler.MemberHandler;
import com.eomcs.pms.handler.ProjectHandler;
import com.eomcs.pms.handler.TaskHandler;
import com.eomcs.util.Prompt;

public class App {

  public static void main(String[] args) {

    List<Board> boardList = new ArrayList<>();
    BoardHandler boardHandler = new BoardHandler(boardList);

    List<Member> memberList = new LinkedList<>();
    MemberHandler memberHandler = new MemberHandler(memberList);

    List<Project> projectList = new LinkedList<>();
    ProjectHandler projectHandler = new ProjectHandler(projectList, memberHandler);

    List<Task> taskList = new ArrayList<>();
    TaskHandler taskHandler = new TaskHandler(taskList, memberHandler);

    // 자바에서는 stack 알고리즘(LIFO)에 대한 인터페이스로 Deque 를 제공한다.
    Deque<String> commandStack = new ArrayDeque<>();

    // 자바에서 제공하는 LinkedList 클래스는 Queue 구현체이기도 하다.
    Queue<String> commandQueue = new LinkedList<>();

    loop:
      while (true) {
        String command = Prompt.inputString("명령> ");

        // 사용자가 입력한 명령을 보관한다.
        commandStack.push(command);
        commandQueue.offer(command);

        switch (command) {
          case "/member/add": memberHandler.add(); break;
          case "/member/list": memberHandler.list(); break;
          case "/member/detail": memberHandler.detail(); break;
          case "/member/update": memberHandler.update(); break;
          case "/member/delete": memberHandler.delete(); break;
          case "/project/add": projectHandler.add(); break;
          case "/project/list": projectHandler.list(); break;
          case "/project/detail": projectHandler.detail(); break;
          case "/project/update": projectHandler.update(); break;
          case "/project/delete": projectHandler.delete(); break;
          case "/task/add": taskHandler.add(); break;
          case "/task/list": taskHandler.list(); break;
          case "/task/detail": taskHandler.detail(); break;
          case "/task/update": taskHandler.update(); break;
          case "/task/delete": taskHandler.delete(); break;
          case "/board/add": boardHandler.add(); break;
          case "/board/list": boardHandler.list(); break;
          case "/board/detail": boardHandler.detail(); break;
          case "/board/update": boardHandler.update(); break;
          case "/board/delete": boardHandler.delete(); break;

          // Iterator 패턴을 이용하면,
          // 자료 구조와 상관없이 일관된 방법으로 목록의 값을 조회할 수 있다.
          case "history": printCommandHistory(commandStack.iterator()); break;
          case "history2": printCommandHistory(commandQueue.iterator()); break;
            
          case "quit":
          case "exit":
            System.out.println("안녕!");
            break loop;
          default:
            System.out.println("실행할 수 없는 명령입니다.");
        }
        System.out.println();
      }

    Prompt.close();
  }

  static void printCommandHistory(Iterator<String> iterator) {
    try {
      int count = 0;
      while (iterator.hasNext()) {
        System.out.println(iterator.next());
        count++;
        if ((count % 5) == 0 && Prompt.inputString(":").equalsIgnoreCase("q")) {
          break;
        }
      }
    } catch (Exception e) {
      System.out.println("history 명령 처리 중 오류 발생!");
    }
  }
}

v.28-a: 커맨드 디자인 패턴 적용: (1) 메서드를 객체로 분리

커맨드 패턴(command pattern)

IMG_0093 IMG_0094 IMG_0092

메서드의 객체화 설계 기법이다. 한 개의 명령어를 처리하는 메서드를 별개의 클래스로 분리하는 기법이다. 이렇게 하면 명령어가 추가될 때마다 새 클래스를 만들면 되기 때문에 기존 코드를 손대지 않아서 유지보수에 좋다. 즉 기존 소스에 영향을 끼치지 않고 새 기능을 추가하는 방식이다. 명령처리를 별도의 객체로 분리하기 때문에 실행 내역을 관리하기 좋고, 각 명령이 수행했던 작업을 다루기가 편하다.

인터페이스를 이용하면 메서드 호출 규칙을 단일화할 수 있기 때문에 코딩의 일관성을 높혀줄 수 있다. 단, 기능을 추가할 때마다 해당 기능을 처리하는 새 클래스가 추가되기 때문에 클래스의 개수는 늘어난다. 그러나 유지보수 관점에서는 소스 코드를 일관성 있게 유지보수 할 수 있는 게 더 중요하다.

커맨드 패턴을 적용하지 않고 새 기능을 추가한다면 메서드를 추가함으로써 기존 코드를 변경해야 한다. 기존 코드에 새 코드를 추가하는 것은 기존 코드를 손댈 수 있는 위험성이 있다. 즉 잘 되던 기능을 망치는 경우가 발생할 수 있다.

실습

1단계: 사용자 명령을 처리하는 메서드의 호출 규칙을 정의한다.

Command 인터페이스를 정의하고 사용자 명령을 처리할 때 호출되는 메서드를 선언한다.

1
2
3
public interface Command {
  public void execute();
}

2단계: 명령어를 처리하는 XxxHandler의 각 메서드를 Command 구현체로 분리한다.

각 명령어를 처리하는 메서드를 별도의 XxxCommand 클래스를 만들어 분리한다. 이 클래스들은 Command 인터페이스 규칙에 따라 정의한다.

1
2
3
4
5
6
7
8
9
10
11
12
public class BoardAddCommand implements Command {
  List<Board> boardList;
  
  public BoardAddCommand(List<Board> list) {
    this.boardList = list;
  }
  
  @Override
  public void execute() {
    // 원래 BoardHandler.add()의 코드였던 것
  }
}

IMG_0101

ProjectHandler.add()의 경우, MemberHandler.findByName()을 사용하고 있었다. 따라서 App에서 ProjectHandler를 생성할 때 파라미터로 MemberHandler 객체를 넘겨줌으로서 메서드를 사용할 수 있게 했다. 그러나 MemberHandler 클래스가 삭제되면서 findByName() 메서드를 사용할 수 없게 되었다. 이에 findByName()메서드를 새로 생긴 MemberListCommand 클래스 내부에 정의했다. 비록 MemberListCommand는 findByName()을 사용하지 않지만, ProjectAddCommand에 memberListCommand 객체 필드를 정의하고, 생성자 파라미터로 외부에 생성된 MemberListCommand 객체를 받을 수 있도록 생성자도 정의하였다. 이로써 ProjectAddCommand는 findByName 메서드를 사용할 수 있게 되었다. 마찬가지의 방법으로 MemberListCommand.findByName 에 의존하는 클래스를 정의하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.eomcs.pms.handler;

import java.util.Iterator;
import java.util.List;
import com.eomcs.pms.domain.Member;

public class MemberListCommand implements Command {

  List<Member> memberList;

  public MemberListCommand(List<Member> list) {
    this.memberList = list;
  }

  @Override
  public void execute() {
    System.out.println("[회원 목록]");

    // 전체 목록을 조회할 때 `Iterator` 객체를 사용한다.
    // 만약 목록의 일부만 조회하면다면 인덱스를 직접 다루는 이전 방식을 사용해야 한다.
    Iterator<Member> iterator = memberList.iterator();

    while (iterator.hasNext()) {
      Member member = iterator.next();
      System.out.printf("%d, %s, %s, %s, %s\n",
          member.getNo(),
          member.getName(),
          member.getEmail(),
          member.getTel(),
          member.getRegisteredDate());
    }
  }

  public Member findByName(String name) {
    for (int i = 0; i < memberList.size(); i++) {
      Member member = memberList.get(i);
      if (member.getName().equals(name)) {
        return member;
      }
    }
    return null;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class ProjectAddCommand implements Command {
  List<Project> projectList;
  MemberListCommand memberListCommand;
  
  public ProjectAddCommnad(List<Project> list, MemberListCommand memberListCommand) {
    this.projectList = projectList;
    this.memberListCommand = memberListCommand;
  }
  
  @Override
  public void execute() {
    System.out.println("[프로젝트 등록]");

    Project project = new Project();
    project.setNo(Prompt.inputInt("번호? "));
    project.setTitle(Prompt.inputString("프로젝트명? "));
    project.setContent(Prompt.inputString("내용? "));
    project.setStartDate(Prompt.inputDate("시작일? "));
    project.setEndDate(Prompt.inputDate("종료일? "));

    while (true) {
      String name = Prompt.inputString("만든이?(취소: 빈 문자열) ");

      if (name.length() == 0) {
        System.out.println("프로젝트 등록을 취소합니다.");
        return;
      } else if (memberListCommand.findByName(name) != null) {
        project.setOwner(name);
        break;
      }

      System.out.println("등록된 회원이 아닙니다.");
    }

    StringBuilder members = new StringBuilder();
    while (true) {
      String name = Prompt.inputString("팀원?(완료: 빈 문자열) ");

      if (name.length() == 0) {
        break;
      } else if (memberListCommand.findByName(name) != null) {
        if (members.length() > 0) {
          members.append(",");
        }
        members.append(name);
      } else {
        System.out.println("등록된 회원이 아닙니다.");
      }
    }
    project.setMembers(members.toString());

    projectList.add(project);
  }
}

3단계: 사용자가 명령어를 입력했을 때 Command 구현체를 실행하도록 변경한다.

App 클래스가 XxxCommand 객체를 통해 처리하도록 변경한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package com.eomcs.pms;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import com.eomcs.pms.domain.Board;
import com.eomcs.pms.domain.Member;
import com.eomcs.pms.domain.Project;
import com.eomcs.pms.domain.Task;
import com.eomcs.pms.handler.BoardAddCommand;
import com.eomcs.pms.handler.BoardDeleteCommand;
import com.eomcs.pms.handler.BoardDetailCommand;
import com.eomcs.pms.handler.BoardListCommand;
import com.eomcs.pms.handler.BoardUpdateCommand;
import com.eomcs.pms.handler.HelloCommand;
import com.eomcs.pms.handler.MemberAddCommand;
import com.eomcs.pms.handler.MemberDeleteCommand;
import com.eomcs.pms.handler.MemberDetailCommand;
import com.eomcs.pms.handler.MemberListCommand;
import com.eomcs.pms.handler.MemberUpdateCommand;
import com.eomcs.pms.handler.ProjectAddCommand;
import com.eomcs.pms.handler.ProjectDeleteCommand;
import com.eomcs.pms.handler.ProjectDetailCommand;
import com.eomcs.pms.handler.ProjectListCommand;
import com.eomcs.pms.handler.ProjectUpdateCommand;
import com.eomcs.pms.handler.TaskAddCommand;
import com.eomcs.pms.handler.TaskDeleteCommand;
import com.eomcs.pms.handler.TaskDetailCommand;
import com.eomcs.pms.handler.TaskListCommand;
import com.eomcs.pms.handler.TaskUpdateCommand;
import com.eomcs.util.Prompt;

public class App {

  public static void main(String[] args) {

    List<Board> boardList = new ArrayList<>();
    BoardAddCommand boardAddCommand = new BoardAddCommand(boardList);
    BoardListCommand boardListCommand = new BoardListCommand(boardList);
    BoardDetailCommand boardDetailCommand = new BoardDetailCommand(boardList);
    BoardUpdateCommand boardUpdateCommand = new BoardUpdateCommand(boardList);
    BoardDeleteCommand boardDeleteCommand = new BoardDeleteCommand(boardList);

    List<Member> memberList = new LinkedList<>();
    MemberAddCommand memberAddCommand = new MemberAddCommand(memberList);
    MemberListCommand memberListCommand = new MemberListCommand(memberList);
    MemberDetailCommand memberDetailCommand = new MemberDetailCommand(memberList);
    MemberUpdateCommand memberUpdateCommand = new MemberUpdateCommand(memberList);
    MemberDeleteCommand memberDeleteCommand = new MemberDeleteCommand(memberList);

    List<Project> projectList = new LinkedList<>();
    ProjectAddCommand projectAddCommand = new ProjectAddCommand(projectList, memberListCommand);
    ProjectListCommand projectListCommand = new ProjectListCommand(projectList);
    ProjectDetailCommand projectDetailCommand = new ProjectDetailCommand(projectList);
    ProjectUpdateCommand projectUpdateCommand = new ProjectUpdateCommand(projectList, memberListCommand);
    ProjectDeleteCommand projectDeleteCommand = new ProjectDeleteCommand(projectList);

    List<Task> taskList = new ArrayList<>();
    TaskAddCommand taskAddCommand = new TaskAddCommand(taskList, memberListCommand);
    TaskListCommand taskListCommand = new TaskListCommand(taskList);
    TaskDetailCommand taskDetailCommand = new TaskDetailCommand(taskList);
    TaskUpdateCommand taskUpdateCommand = new TaskUpdateCommand(taskList, memberListCommand);
    TaskDeleteCommand taskDeleteCommand = new TaskDeleteCommand(taskList);

    HelloCommand helloCommand = new HelloCommand();

    // 자바에서는 stack 알고리즘(LIFO)에 대한 인터페이스로 Deque 를 제공한다.
    Deque<String> commandStack = new ArrayDeque<>();

    // 자바에서 제공하는 LinkedList 클래스는 Queue 구현체이기도 하다.
    Queue<String> commandQueue = new LinkedList<>();

    loop:
      while (true) {
        String command = Prompt.inputString("명령> ");

        // 사용자가 입력한 명령을 보관한다.
        commandStack.push(command);
        commandQueue.offer(command);

        switch (command) {
          case "/member/add": memberAddCommand.execute(); break;
          case "/member/list": memberListCommand.execute(); break;
          case "/member/detail": memberDetailCommand.execute(); break;
          case "/member/update": memberUpdateCommand.execute(); break;
          case "/member/delete": memberDeleteCommand.execute(); break;
          case "/project/add": projectAddCommand.execute(); break;
          case "/project/list": projectListCommand.execute(); break;
          case "/project/detail": projectDetailCommand.execute(); break;
          case "/project/update": projectUpdateCommand.execute(); break;
          case "/project/delete": projectDeleteCommand.execute(); break;
          case "/task/add": taskAddCommand.execute(); break;
          case "/task/list": taskListCommand.execute(); break;
          case "/task/detail": taskDetailCommand.execute(); break;
          case "/task/update": taskUpdateCommand.execute(); break;
          case "/task/delete": taskDeleteCommand.execute(); break;
          case "/board/add": boardAddCommand.execute(); break;
          case "/board/list": boardListCommand.execute(); break;
          case "/board/detail": boardDetailCommand.execute(); break;
          case "/board/update": boardUpdateCommand.execute(); break;
          case "/board/delete": boardDeleteCommand.execute(); break;
          case "/hello": helloCommand.execute(); break;

          // Iterator 패턴을 이용하면,
          // 자료 구조와 상관없이 일관된 방법으로 목록의 값을 조회할 수 있다.
          case "history": printCommandHistory(commandStack.iterator()); break;
          case "history2": printCommandHistory(commandQueue.iterator()); break;

          case "quit":
          case "exit":
            System.out.println("안녕!");
            break loop;
          default:
            System.out.println("실행할 수 없는 명령입니다.");
        }
        System.out.println(); // 이전 명령의 실행을 구분하기 위해 빈 줄 출력
      }

    Prompt.close();
  }

  static void printCommandHistory(Iterator<String> iterator) {
    try {
      int count = 0;
      while (iterator.hasNext()) {
        System.out.println(iterator.next());
        count++;

        // 5개 출력할 때 마다 계속 출력할지 묻는다.
        if ((count % 5) == 0 && Prompt.inputString(":").equalsIgnoreCase("q")) {
          break;
        }
      }
    } catch (Exception e) {
      System.out.println("history 명령 처리 중 오류 발생!");
    }
  }
}

v.28-b: 커맨드 디자인 패턴 적용: (2) Map을 이용한 커맨드 객체 관리

컬렉션 API의 Map 객체를 이요해서 커맨드 객체를 관리한다.

실습

  • 낱개의 레퍼런스로 커맨드 객체를 관리하던 방식을 Map을 이용하여 관리한다.
  • 명령어를 처리할 때는 ` Map`에서 커맨드 객체를 찾아 실행한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package com.eomcs.pms;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import com.eomcs.pms.domain.Board;
import com.eomcs.pms.domain.Member;
import com.eomcs.pms.domain.Project;
import com.eomcs.pms.domain.Task;
import com.eomcs.pms.handler.BoardAddCommand;
import com.eomcs.pms.handler.BoardDeleteCommand;
import com.eomcs.pms.handler.BoardDetailCommand;
import com.eomcs.pms.handler.BoardListCommand;
import com.eomcs.pms.handler.BoardUpdateCommand;
import com.eomcs.pms.handler.Command;
import com.eomcs.pms.handler.HelloCommand;
import com.eomcs.pms.handler.MemberAddCommand;
import com.eomcs.pms.handler.MemberDeleteCommand;
import com.eomcs.pms.handler.MemberDetailCommand;
import com.eomcs.pms.handler.MemberListCommand;
import com.eomcs.pms.handler.MemberUpdateCommand;
import com.eomcs.pms.handler.ProjectAddCommand;
import com.eomcs.pms.handler.ProjectDeleteCommand;
import com.eomcs.pms.handler.ProjectDetailCommand;
import com.eomcs.pms.handler.ProjectListCommand;
import com.eomcs.pms.handler.ProjectUpdateCommand;
import com.eomcs.pms.handler.TaskAddCommand;
import com.eomcs.pms.handler.TaskDeleteCommand;
import com.eomcs.pms.handler.TaskDetailCommand;
import com.eomcs.pms.handler.TaskListCommand;
import com.eomcs.pms.handler.TaskUpdateCommand;
import com.eomcs.util.Prompt;

public class App {

  public static void main(String[] args) {

    // 커맨드 객체를 저장할 맵 객체를 준비한다.
    Map<String,Command> commandMap = new HashMap<>();

    // 맵 객체에 커맨드 객체를 보관한다.
    List<Board> boardList = new ArrayList<>();
    commandMap.put("/board/add", new BoardAddCommand(boardList));
    commandMap.put("/board/list", new BoardListCommand(boardList));
    commandMap.put("/board/detail", new BoardDetailCommand(boardList));
    commandMap.put("/board/update", new BoardUpdateCommand(boardList));
    commandMap.put("/board/delete", new BoardDeleteCommand(boardList));

    List<Member> memberList = new LinkedList<>();
    MemberListCommand memberListCommand = new MemberListCommand(memberList);
    commandMap.put("/member/add", new MemberAddCommand(memberList));
    commandMap.put("/member/list", memberListCommand);
    commandMap.put("/member/detail", new MemberDetailCommand(memberList));
    commandMap.put("/member/update", new MemberUpdateCommand(memberList));
    commandMap.put("/member/delete", new MemberDeleteCommand(memberList));

    List<Project> projectList = new LinkedList<>();
    commandMap.put("/project/add", new ProjectAddCommand(projectList, memberListCommand));
    commandMap.put("/project/list", new ProjectListCommand(projectList));
    commandMap.put("/project/detail", new ProjectDetailCommand(projectList));
    commandMap.put("/project/update", new ProjectUpdateCommand(projectList, memberListCommand));
    commandMap.put("/project/delete", new ProjectDeleteCommand(projectList));

    List<Task> taskList = new ArrayList<>();
    commandMap.put("/task/add", new TaskAddCommand(taskList, memberListCommand));
    commandMap.put("/task/list", new TaskListCommand(taskList));
    commandMap.put("/task/detail", new TaskDetailCommand(taskList));
    commandMap.put("/task/update", new TaskUpdateCommand(taskList, memberListCommand));
    commandMap.put("/task/delete", new TaskDeleteCommand(taskList));
    commandMap.put("/hello", new HelloCommand());

    Deque<String> commandStack = new ArrayDeque<>();
    Queue<String> commandQueue = new LinkedList<>();

    loop:
      while (true) {
        String inputStr = Prompt.inputString("명령> ");

        if (inputStr.length() == 0) {
          continue;
        }

        // 사용자가 입력한 명령을 보관한다.
        commandStack.push(inputStr);
        commandQueue.offer(inputStr);

        switch (inputStr) {
          case "history": printCommandHistory(commandStack.iterator()); break;
          case "history2": printCommandHistory(commandQueue.iterator()); break;
          case "quit":
          case "exit":
            System.out.println("안녕!");
            break loop;
          default:
            Command command = commandMap.get(inputStr);
            if (command != null) {
              command.execute();
            } else {
              System.out.println("실행할 수 없는 명령입니다.");
            }
        }
        System.out.println(); 
      }

    Prompt.close();
  }

  static void printCommandHistory(Iterator<String> iterator) {
    try {
      int count = 0;
      while (iterator.hasNext()) {
        System.out.println(iterator.next());
        count++;

        if ((count % 5) == 0 && Prompt.inputString(":").equalsIgnoreCase("q")) {
          break;
        }
      }
    } catch (Exception e) {
      System.out.println("history 명령 처리 중 오류 발생!");
    }
  }
}

This post is licensed under CC BY 4.0 by the author.

학원 #35일차: 중첩 클래스: static nested class, inner class, local class

💻 HTTP #5: HTTP의 진화

Loading comments from Disqus ...