Publish:

νƒœκ·Έ: ,

μΉ΄ν…Œκ³ λ¦¬:

  • 이전 글에 μ΄μ–΄μ„œ 메뉴λ₯Ό Stack 으둜 κ΄€λ¦¬ν•˜λ„λ‘ μˆ˜μ •ν•΄ λ³Έλ‹€.
  • νŒ©ν† λ¦¬λ©”μ†Œλ“œλ₯Ό μ μš©ν•΄ 객체 생성에 κ΄€ν•œ μ½”λ“œλ₯Ό 숨기고, μ’€ 더 μ˜λ―ΈμžˆλŠ” μ΄λ¦„μ˜ λ©”μ†Œλ“œλ₯Ό μ œκ³΅ν•œλ‹€.

Stack κ΅¬ν˜„

어디에 μ €μž₯ν•˜λŠλƒκ°€ μ•„λ‹ˆλΌ μ–΄λ–»κ²Œ κ°€μ Έμ˜¬ 것인가에 μ΄ˆμ μ„ λ§žμΆ°μ„œ μ§„ν–‰ν•˜λ―€λ‘œ, 기쑴에 λ§Œλ“€μ—ˆλ˜ λ§ν¬λ“œλ¦¬μŠ€νŠΈλ₯Ό μ΄μš©ν•΄ Stack 을 κ°„λ‹¨νžˆ κ΅¬ν˜„ν•œλ‹€. 배열을 μ΄μš©ν•΄ κ΅¬ν˜„ν•΄λ„ 상관없닀.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Stack<E> extends LinkedList<E> {

  public E push(E value) {
    add(value);
    return value;
  }

  public E pop() {
    return remove(size() - 1);
  }

  public E peek() {
    return get(size() - 1);
  }

  public boolean empty() {
    return size() == 0;
  }
}

μŠ€νƒμ˜ 기본적인 νŠΉμ§•μ€ ν›„μž…μ„ μΆœ(Last-In First-Out)이닀. 이와 같은 νŠΉμ§•μ€ μ›ΉλΈŒλΌμš°μ € 탐색 μ‹œ λ’€λ‘œκ°€κΈ°λ‚˜ 되돌리기(ctrl + z)와 같은 κΈ°λŠ₯을 κ΅¬ν˜„ν•  λ•Œ μš©μ΄ν•˜λ‹€. Stack 에 λ„£μ„λ•ŒλŠ” 순차적으둜 λ“€μ–΄κ°€κ³  Stack μ—μ„œ λΉ μ Έλ‚˜μ˜¬λ•ŒλŠ” κ°€μž₯ 졜근 μž‘μ—…λΆ€ν„° μ—­μˆœμœΌλ‘œ λΉ μ Έλ‚˜μ˜¨λ‹€.

이λ₯Ό 메뉴 μ§„μž… μ‹œ λ³΄μ—¬μ£ΌλŠ” Prompt 에 μ μš©ν•΄ 보자. 메뉴λ₯Ό μ§„μž…ν• λ•Œ 순차적으둜 λ‚˜νƒ€λ‚΄κ³ , 또 μ΄μ „μœΌλ‘œ λŒμ•„κ°ˆλ•ŒλŠ” κ·Έ μ—­μˆœμœΌλ‘œ 보여쀀닀.

As-is

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  [메인]
  1. 과제
  2. κ²Œμ‹œκΈ€
  3. νšŒμ›
  4. κ°€μž…μΈμ‚¬
  5. 도움말
  0. 이전
  메인> 1
  
  [과제]
  1. 등둝
  2. 쑰회
  3. λ³€κ²½
  4. μ‚­μ œ
  5. λͺ©λ‘
  0. 이전
  과제>        <---- 이 뢀뢄을 Stack 으둜 κ΄€λ¦¬ν•œλ‹€.

메인 λ©”λ‰΄μ—μ„œ ν•˜μœ„ λ©”λ‰΄λ‘œ μ§„μž… μ‹œ μž…λ ₯ 창에 ν˜„μž¬ μ–΄λ””κΉŒμ§€ μ§„μž…ν–ˆλŠ”μ§€ ν‘œμ‹œκ°€ λ˜μ§€ μ•Šκ³  μžˆλ‹€. MenuGroup μƒμ„±μžμ—μ„œ title 을 λ°›μ•„ κ·ΈλŒ€λ‘œ 좜λ ₯만 ν•΄μ£Όκ³  있기 λ•Œλ¬Έμ΄λ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MenuGroup extends AbstractMenu {

  // μƒμ„±μžμ—μ„œ title λ°›μŒ
  public MenuGroup(String title) {
    super(title);
  }

  @Override
  public void execute(Prompt prompt) {
    this.printMenu();

    while (true) {  // title μ‚¬μš©
      String input = prompt.input("%s> ", this.getTitle()); 

          ...
    }
  }
}

App

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class App {
  public static void main(String[] args) throws Exception {
    Prompt prompt = new Prompt(System.in);

    ArrayList<Board> boardRepository = new ArrayList<>();
    ArrayList<Assignment> assignmentRepository = new ArrayList<>();
    ArrayList<Member> memberRepository = new ArrayList<>();
    ArrayList<Board> greetingRepository = new ArrayList<>();

    MenuGroup mainMenu = new MenuGroup("메인");

    MenuGroup boardMenu = new MenuGroup("κ²Œμ‹œκΈ€");
    boardMenu.add(new MenuItem("등둝", new BoardAddHandler(boardRepository, prompt)));
    boardMenu.add(new MenuItem("쑰회", new BoardViewHandler(boardRepository, prompt)));
    boardMenu.add(new MenuItem("λ³€κ²½", new BoardModifyHandler(boardRepository, prompt)));
    boardMenu.add(new MenuItem("μ‚­μ œ", new BoardDeleteHandler(boardRepository, prompt)));
    boardMenu.add(new MenuItem("λͺ©λ‘", new BoardListHandler(boardRepository, prompt)));
    mainMenu.add(boardMenu);
    
        ....
  }
}

Appν΄λž˜μŠ€μ—μ„œ 직접 객체λ₯Ό μƒμ„±ν•˜λŠ” μ½”λ“œκ°€ 많이 ν¬ν•¨λ˜μ–΄ μžˆλ‹€. μ™œ 객체λ₯Ό 생성해야 ν•˜λŠ”μ§€ λΆˆλΆ„λͺ…ν•˜κ³  ꡳ이 μ•Œ ν•„μš”λ„ μ—†λ‹€. μ½”λ“œμ˜ 가독성이 떨어지고 λ³΅μž‘ν•΄ 보인닀.

To-be

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
public class MenuGroup extends AbstractMenu {
      ...
  
  // μƒμ„±μžμ—μ„œ Stack 을 λ°›λŠ”λ‹€.
  private MenuGroup(String title, Stack<String> breadcrumb) {
    super(title, breadcrumb);
  }

  // νŒ©ν† λ¦¬ λ©”μ„œλ“œλ₯Ό μ΄μš©ν•΄ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•œλ‹€.
  public static MenuGroup getInstance(String title) {
    return new MenuGroup(title, new Stack<>());
  }

  @Override
  public void execute(Prompt prompt) {
    // 메뉴λ₯Ό μ‹€ν–‰ν•  λ•Œ λ©”λ‰΄μ˜ 제λͺ©μ„ breadcrumb 에 μΆ”κ°€ν•œλ‹€.
    breadcrumb.push(title);
    this.printMenu();

    while (true) {
      String input = prompt.input("%s> ", this.getMenuPath());

      if (input.equals("menu")) {
        this.printMenu();
        continue;
      } else if (input.equals("0")) {
        break;
      }

      try {
        int menuNo = Integer.parseInt(input);
        if (menuNo < 1 || menuNo > menus.size()) {
          System.out.println("메뉴 λ²ˆν˜Έκ°€ μ˜³μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.");
          continue;
        }

        this.menus.get(menuNo - 1).execute(prompt);  // μ‹€ν–‰ν• λ•Œλ§ˆλ‹€ Stack 에 push λœλ‹€.
      } catch (Exception e) {
        System.out.println("메뉴가 μ˜³μ§€ μ•ŠμŠ΅λ‹ˆλ‹€");
      }
    }

    breadcrumb.pop(); // 메뉴 λ‚˜κ°ˆλ•Œ 메뉴 제λͺ©μ„ μ œκ±°ν•œλ‹€.
  }
  
  // App 에 쒀더 직관적인 μ½”λ“œλ₯Ό μ œκ³΅ν•˜κΈ° μœ„ν•œ νŽΈμ˜μ„± λ©”μ†Œλ“œ
  public MenuItem addItem(String title, MenuHandler handler) {
    MenuItem menuItem = new MenuItem(title, handler, breadcrumb);
    add(menuItem);
    return menuItem;
  }

  // App 에 쒀더 직관적인 μ½”λ“œλ₯Ό μ œκ³΅ν•˜κΈ° μœ„ν•œ νŽΈμ˜μ„± λ©”μ†Œλ“œ
  public MenuGroup addGroup(String title) {
    MenuGroup menuGroup = new MenuGroup(title, breadcrumb);
    add(menuGroup);
    return menuGroup;
  }
}

Appμ—μ„œ MenuGroup을 ν˜ΈμΆœν• λ•Œ Stack 을 λ§Œλ“€μ–΄ AbstractMenu둜 전달해 μ€€λ‹€. 객체 생성에 λŒ€ν•œ μ½”λ“œλŠ” νŒ©ν† λ¦¬ λ©”μ†Œλ“œλ₯Ό μ΄μš©ν•œλ‹€.

μƒμ„±μžλ₯Ό 직접 ν˜ΈμΆœν•˜κΈ° 보닀 μ˜λ―ΈμžˆλŠ” 이름 을 가진 νŒ©ν† λ¦¬ λ©”μ†Œλ“œλ₯Ό 톡해 λ°˜ν™˜ν•˜λ„λ‘ ν•˜λŠ”κ²ƒμ΄ μ½”λ“œ 가독성에 μ’‹λ‹€. λ˜ν•œ μƒμ„±μžλ₯Ό private둜 μ„€μ •ν•΄ νŒ©ν† λ¦¬ λ©”μ†Œλ“œμ—μ„œλ§Œ 객체 생성이 κ°€λŠ₯ν•˜λ„λ‘ ν•  수 μžˆλ‹€.

Appν΄λž˜μŠ€μ—μ„œ μ•„λž˜μ™€κ°™μ΄ getInstanceλ₯Ό ν˜ΈμΆœν•˜λ©΄μ„œ β€œλ©”μΈβ€ μ΄λΌλŠ” 메뉴 제λͺ©μ„ μƒμ„±μžλ‘œ μ£Όμž…ν•œλ‹€. μ΄λ•Œ Stack 객체도 ν•¨κ»˜ λ§Œλ“€μ–΄ μ£Όμž…ν•΄μ€€λ‹€.

1
  MenuGroup mainMenu = MenuGroup.getInstance("메인");

메뉴 μž…λ ₯을 λ°›λŠ” excuteκ°€ 졜초 호좜되면 μœ„ μ½”λ“œμ—μ„œ μƒμ„±μžλ‘œ λ°›μ•„μ„œ ν•„λ“œλ‘œ 가지고 있던 메뉴 title 을 Stack 에 λ„£λŠ”λ‹€. ν˜„μž¬ Stack μ—λŠ” β€œλ©”μΈβ€μ΄ λ“€μ–΄κ°€ μžˆλŠ” μƒνƒœκ°€ λœλ‹€.

1
2
  int menuNo = Integer.parseInt(input);
  this.menus.get(menuNo - 1).execute(prompt);

이후 μž…λ ₯ν•œ 메뉴 λ²ˆν˜Έμ— λ”°λ₯Έ title 이 κ³„μ†ν•΄μ„œ Stack 에 push λœλ‹€. breadcrumb 은 getMenuPath()μ—μ„œ λ§Œλ“€μ–΄μ§„λ‹€.

1
2
3
4
  // Stack μ—μ„œ 메뉴λͺ©λ‘μ„ 가져와 μ‘°λ¦½ν•œλ‹€.
  public String getMenuPath() {
    return String.join("/", breadcrumb.toArray(new String[0]));
  }

menus λͺ©λ‘μ€ App ν΄λž˜μŠ€μ—μ„œ addItemκ³Ό addGroup이 호좜될 λ•Œλ§ˆλ‹€ μ €μž₯λœλ‹€.

1
2
3
4
5
6
7
8
  MenuGroup mainMenu = MenuGroup.getInstance("메인");

  MenuGroup boardMenu = mainMenu.addGroup("κ²Œμ‹œκΈ€");
  boardMenu.addItem("등둝", new BoardAddHandler(boardRepository, prompt));
  boardMenu.addItem("쑰회", new BoardViewHandler(boardRepository, prompt));
  boardMenu.addItem("λ³€κ²½", new BoardModifyHandler(boardRepository, prompt));
  boardMenu.addItem("μ‚­μ œ", new BoardDeleteHandler(boardRepository, prompt));
  boardMenu.addItem("λͺ©λ‘", new BoardListHandler(boardRepository, prompt));

App

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
public class App {

  public static void main(String[] args) throws Exception {
    Prompt prompt = new Prompt(System.in);

    List<Board> boardRepository = new LinkedList<>();
    List<Assignment> assignmentRepository = new LinkedList<>();
    List<Member> memberRepository = new ArrayList<>();
    List<Board> greetingRepository = new ArrayList<>();

    MenuGroup mainMenu = MenuGroup.getInstance("메인");
    
    MenuGroup boardMenu = mainMenu.addGroup("κ²Œμ‹œκΈ€");
    boardMenu.addItem("등둝", new BoardAddHandler(boardRepository, prompt));
    boardMenu.addItem("쑰회", new BoardViewHandler(boardRepository, prompt));
    boardMenu.addItem("λ³€κ²½", new BoardModifyHandler(boardRepository, prompt));
    boardMenu.addItem("μ‚­μ œ", new BoardDeleteHandler(boardRepository, prompt));
    boardMenu.addItem("λͺ©λ‘", new BoardListHandler(boardRepository, prompt));

    MenuGroup memberMenu = mainMenu.addGroup("νšŒμ›");
    memberMenu.addItem("등둝", new MemberAddHandler(memberRepository, prompt));
    memberMenu.addItem("쑰회", new MemberViewHandler(memberRepository, prompt));
    memberMenu.addItem("λ³€κ²½", new MemberModifyHandler(memberRepository, prompt));
    memberMenu.addItem("μ‚­μ œ", new MemberDeleteHandler(memberRepository, prompt));
    memberMenu.addItem("λͺ©λ‘", new MemberListHandler(memberRepository, prompt));

    MenuGroup greetingMenu = mainMenu.addGroup("κ°€μž…μΈμ‚¬");
    greetingMenu.addItem("등둝", new BoardAddHandler(greetingRepository, prompt));
    greetingMenu.addItem("쑰회", new BoardViewHandler(greetingRepository, prompt));
    greetingMenu.addItem("λ³€κ²½", new BoardModifyHandler(greetingRepository, prompt));
    greetingMenu.addItem("μ‚­μ œ", new BoardDeleteHandler(greetingRepository, prompt));
    greetingMenu.addItem("λͺ©λ‘", new BoardListHandler(greetingRepository, prompt));

    mainMenu.addItem("도움말", new HelpHandler(prompt));

    // jvm κΉŒμ§€ 였λ₯˜λ³΄κ³  μ•ˆκ°€κ²Œ 막기
    while (true) {
      try {
        mainMenu.execute(prompt);
        prompt.close();
        break;
      } catch (Exception e) {
        System.out.println("main() μ˜ˆμ™Έ λ°œμƒ");
      }
    }

μ—”νŠΈλ¦¬ν¬μΈνŠΈκ°€ λ˜λŠ” App ν΄λž˜μŠ€μ—μ„œμ˜ μ½”λ“œκ°€ μ’€ 더 직관적이고 의미λ₯Ό μ•Œμ•„λ³Ό 수 μžˆλŠ” λ©”μ†Œλ“œλ“€λ‘œ λ³€ν–ˆλ‹€.

μ†ŒμŠ€μ½”λ“œ

https://bit.ly/48Abpgy

λ°©λ¬Έν•΄ μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€! λŒ“κΈ€,지적,ν”Όλ“œλ°± μ–Έμ œλ‚˜ ν™˜μ˜ν•©λ‹ˆλ‹€πŸ˜Š

λŒ“κΈ€λ‚¨κΈ°κΈ°