* Stack, Queue 자료구조 구현
BreadCrumb (빵 부스러기) 를 이용해서 지나온 경로를 사용자에게 표시해준다.
이렇게 함으로써 현재 어떠한 메뉴를 이용하고 있는 지를 시각화해줄 수 있다.
예 : 메인/회원>
* Stack 구동원리
Stack 은 LIFO (Last In First Out)이다.
스택에 값이 들어오게 되고 ,
후입선출로서 나중에 들어온 값이 먼저 빠져나가게 된다.
예) c:/>사용자>bitcamp>git>bitcamp-study
나갈 때의 순서는 bitcamp-study > git > bitcamp 사용자 순서이다.
* Stack 구현
자식 클래스는 부모 클래스의 메서드를 사용할 수 있다.
또한 부모 클래스가 구현한 객체는 모든 자식 클래스의 설계도를 바탕으로
객체를 생성하여 인스턴스 변수를 사용할 수 있다.
* 상속과 인스턴스 필드들
student class 는 Member 클래스를 상속받았기 때문에
Strundent를 설계도로 한 객체 s는
Member의 인스턴스 변수 정보를 갖고 있다.
* Stack 인스턴스
Stack class 는 LinkedList 클래스를 상속받았기 때문에 Stack 를 설계도로 한 객체 s는
LinkedList 의 인스턴스 변수 정보를 갖고 있다.
즉 Stack에는 push() 메소드가 add() 라는 메소드를 사용하기때문에
LinkedList의 메서드를 사용하여 기능을 구현하여 head, tail, size 의 인스턴스 변수를 사용하게 된다.
이 인스턴스 변수는 LinkedList의 인스턴스 변수가 아니라 Stack 내부에서 정의된 인스턴스 변수이다.
* Stack, Queue 자료구조 구현
Breadcrumb 를 적용시키기 위해서
MenuPrompt라는 클래스를 생성하고
inputMenu 메서드를 구성한다.
기존의 Prompt 클래스의 기능을 그대로 구현해주기 위해서,
반드시 기존 Prompt를 상속받아서 그 메서드 기능을 내부에서 구현해주어야한다.
이후에 기존 app.java, MemberHandler, BoardHandler 등 Prompt 를 import해서 메서드를 사용하는
클래스들을 수정해주어야한다.
메뉴에 진입 할 때 (1,2,3)
prompt.appendBreadcrumb(this.title);
메뉴에서 빠져나올 때 (0)
prompt.removeBreadcrumb();
MenuPrompt 클래스
package bitcamp.util;
public class MenuPrompt extends Prompt {
private Stack breadcrumbs = new Stack();
public void appendBreadcrumb(String title) {
breadcrumbs.push(title);
}
public void removeBreadcrumb() {
breadcrumbs.pop();
}
public String inputMenu() {
StringBuilder titleBuilder = new StringBuilder();// 예) 메인/회원>
for (int i = 0; i < breadcrumbs.size(); i++) {
if (titleBuilder.length() > 0) {
titleBuilder.append("/");
// 기존에 경로가 있다면 구분해주기 위해서 "/"를 표시
}
titleBuilder.append(breadcrumbs.get(i));
}
titleBuilder.append("> ");
return this.inputString(titleBuilder.toString());
}
}
* Queue 구현과 적용
Queue 구현을 인터페이스 규칙에 따라서 생성하기
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Queue.html
public void offer(Object value) {
this.add(value);
}
public Object poll() {
if (this.size() == 0) {
return null;
}
return this.remove(0);
}
* Stack 구현
Steck 구현을 인터페이스 규칙에 따라서 생성하기
public void push(Object value) {
// 목록 맨 끝에 추가한다.
// 따로 만들 필요가 없다.
// 수퍼 클래스에 있는 메서드를 이용하여 기능을 구현한다.
this.add(value); // 상속받은 메서드
// 서브 클래스에서 사용할 수 있는 수퍼 클래스의 메서드
}
public Object pop() {
if (this.empty()) {
return null;
}
return this.remove(this.size() - 1);
}
public Object peek() {
if (this.empty()) {
return null;
}
return this.get(this.size() - 1);
}
public boolean empty() {
return this.size() == 0;
}
Stack 클래스 설명
push 메서드:
push 메서드는 주어진 요소를 스택의 맨 끝에 추가
현재 구현은 add 메서드를 호출하여 요소를 추가하고 있습니다.
상속받은 add 메서드를 이용하여 스택의 동작을 구현하는 것은 올바른 방법입니다.
pop 메서드:
pop 메서드는 스택의 맨 끝에 있는 요소를 제거하고 반환
현재 구현은 remove 메서드를 호출하여 맨 끝에 있는 요소를 제거하고 반환하고 있습니다.
empty 메서드를 사용하여 스택이 비어있는지 여부를 확인하고 처리하는 부분이 잘 구현되어 있습니다.
peek 메서드:
peek 메서드는 스택의 맨 끝에 있는 요소를 조회
현재 구현은 get 메서드를 호출하여 맨 끝에 있는 요소를 반환하고 있습니다.
empty 메서드:
empty 메서드는 스택이 비어있는지 여부를 반환합니다.
현재 구현은 size 메서드를 호출하여 스택의 크기를 확인하고, 크기가 0인지 여부를 반환하고 있습니다.
이렇게 생성된 스택과 큐는 MenuPrompt 클래스를 구성하는데 사용된다.
MenuPrompt 는 Menu와 관련한 기능을 한다.
인스턴스 변수로 Queue, Stack의 설계도에 따른 파라미터 객체
commandHistory, menus, breadcrumbs 를 사용한다.
Queue, Stack 는 LinkedList의 서브 클래스이므로 상속 받은 메서드를 사용할 수 있다.
또한 LinkedList는 수퍼 클래스 List의 인터페이스 규칙인 add, get, toArray, remove(object), remove (boolean)을
상속 받아 사용한다.
MenuPrompt의 기능
MenuPrompt의 기능을 하는 메서드는 다음과 같다.
appendBreadcrumb(String title, String menu)
메서드 설명: 현재 경로와 메뉴를 스택에 추가
기능: 현재 경로와 해당 메뉴를 breadcrumbs 스택과 menus 스택에 각각 추가
removeBreadcrumb()
메서드 설명: 현재 경로와 메뉴를 스택에서 제거
기능: breadcrumbs 스택과 menus 스택에서 현재 경로와 해당 메뉴를 각각 제거
printMenu()
메서드 설명: 현재 메뉴를 출력
기능: menus 스택에서 최상단에 있는 메뉴를 출력
printCommandHistory()
메서드 설명: 사용자의 명령어 히스토리를 출력
기능: commandHistory 큐에 저장된 사용자의 명령어 히스토리를 출력
inputMenu()
메서드 설명: 사용자로부터 메뉴 입력을 받음
기능: 사용자로부터 메뉴를 입력받아 유효성을 검사하고,
"history"나 "menu"인 경우에는 각각의 메서드를 호출하여 히스토리 또는 현재 메뉴를 출력
그 외에는 입력받은 메뉴를 반환
findMenuItem(String command)
메서드 설명: 입력받은 메뉴 번호에 해당하는 메뉴를 찾음
기능: 현재 메뉴에서 입력받은 메뉴 번호와 일치하는 메뉴를 찾아 반환
MenuPrompt의 코드
package bitcamp.util;
public class MenuPrompt extends Prompt {
private Queue commandHistory = new Queue();
private Stack menus = new Stack();
private Stack breadcrumbs = new Stack();
public void appendBreadcrumb(String title, String menu) {
this.breadcrumbs.push(title);
this.menus.push(menu);
}
public void removeBreadcrumb() {
this.breadcrumbs.pop();
this.menus.pop();
}
public void printMenu() {
System.out.println(this.menus.peek());
}
public void printCommandHistroy() {
for (int i = 0; i < this.commandHistory.size(); i++) {
System.out.println(this.commandHistory.get(i));
}
}
public String inputMenu() {
StringBuilder titleBuilder = new StringBuilder();// 예) 메인/회원>
for (int i = 0; i < this.breadcrumbs.size(); i++) {
if (titleBuilder.length() > 0) {
titleBuilder.append("/");
// 기존에 경로가 있다면 구분해주기 위해서 "/"를 표시
}
titleBuilder.append(this.breadcrumbs.get(i));
}
titleBuilder.append("> ");
String command = null;
while (true) {
command = this.inputString(titleBuilder.toString());
if (command.equals("history")) {
printCommandHistroy();
} else if (command.equals("menu")) {
this.printMenu();
} else if (findMenuItem(command) == null) {
System.out.println("메뉴 번호가 옳지 않습니다.");
} else {
break;
}
}
if (this.commandHistory.size() == 10) {
// 사용자가 입력한 명령어를 history에 보관
// 명령어 목록은 최대 10개만 유지한다.
// 10개를 초과할 경우 맨 앞에 기록을 삭제한다.
this.commandHistory.poll();
}
String menuItem = findMenuItem(command);
if (menuItem != null) {
this.commandHistory.offer(titleBuilder.toString() + ": " + menuItem);
} else {
this.commandHistory.offer(command);
}
return command;
}
private String findMenuItem(String command) {
String menuTitle = null;
// 메뉴에서 command에 해당하는 메뉴가 있다면 그 메뉴 이름을 리턴하고
// 없다면 null을 리턴한다.
// 1) 현재 메뉴를 알아낸다. 메뉴 스택에서 맨 마지막에 입력한 메뉴를 조회한다.
String menu = (String) this.menus.peek();
// 2) 꺼낸 메뉴에서 해당 번호의 메뉴를 찾는다.
String[] menuItems = menu.split("\n");
for (String menuItem : menuItems) {
if (menuItem.startsWith(command)) {
return menuItem;
}
}
return menuTitle;
}
}
내일 배우는 것
1. 메뉴구성
디자인 패턴 - composite 패턴
시작은 루트메뉴.
하나의 메뉴가 다른 메뉴를 포함한다.
2. 디자인 패턴 - 커맨드 패턴
메서드들을 클래스로 분리한다. Member(5개) Board(5개) = 10개
서블릿 프로그램을 배울 때 핵심 기술로 필요하다.
3. 입력
디자인 패턴 - observer 패턴
메뉴와 클래스를 연결하는 것은 observer 패턴을 이용한다.
메뉴가 추가될 때 클래스를 추가한다.
이전에는 MemberHanlder 클래스를 열어서
search 라는 기능을 포함한 메서드를 추가하는데,
search 멤버 서치 커맨드 클래스를 만든다.
이렇게하면 새로운 코드를 추가할 때 기존 코드를 수정하지 않아도 된다.