// 출처: codefights.com
//
// 장애물들의 x 좌표(양의 좌표)가 배열로 주어질 때,
// 0부터 오른쪽으로 동일한 거리를 점프하여 장애물들을 피한다면,
// 점프해야 할 최소 거리는 얼마인지 구하라!
//
// 예)
// [5, 3, 6, 7, 9] ==> 4
//
/*
You are given an array of integers representing coordinates of obstacles
situated on a straight line.
Assume that you are jumping from the point with coordinate 0 to the right.
You are allowed only to make jumps of the same length represented
by some integer.
Find the minimal length of the jump enough to avoid all the obstacles.
Example
for [5, 3, 6, 7, 9] output should be 4
[input] array.integer inputArray
non-empty array of positive integers
[output] integer
the desired length
*/
//
// [시간 복잡도]
// - ?
//
public class Test08 {
public static void main(String[] args) {
System.out.println(avoidObstacles(new int[]{5,3,6,7,9,13,11}) == 4);
}
static int avoidObstacles(int[] inputArray) {
// 이 메서드를 완성하시오!
int ans = 1;
// 점프하는 칸 수가 배열에 주어진 장애물의 좌표의 배수인가?
// 배열에 주어진장애물들의 좌표 중에서 점프하는 칸 수의 배수가 있으면 안된다.
// 즉, 나누어 떨어지면 안된다는 말이다.
for(int i:inputArray) {
if(i%ans==0) {
ans++;
}
}
return ans;
}
}
public class Exam0120 {
interface ProtocolA {
void rule1();
void rule2();
void rule3();
void rule4();
}
// 추상클래스에서 인터페이스의 규칙을 모두 미리 구현해 둔다.
// 물론 최소 상태로 구현한다.
abstract class AbstractProtocolA implements ProtocolA {
@Override
public void rule1() {}
@Override
public void rule2() {}
@Override
public void rule3() {}
@Override
public void rule4() {}
}
// 인터페이스를 준수하는 클래스를 만들어야 할 경우,
// 직접 인터페이스를 구현하는 대신에
// 다음과 같이 추상 클래스를 상속 받는다.
// - 수퍼 클래스에서 인터페이스를 구현했다면,
// 그 서브 클래스들도 인터페이스를 구현한 것이 된다.
class ProtocolAImpl extends AbstractProtocolA {
// 이 방식의 장점은 오버라이딩을 통해
// 인터페이스 규칙 중 필요한 규칙만 구현할 수 있다.
@Override
public void rule2() {
System.out.println("ProtocolAImpl.rule2()");
}
}
void test() {
ProtocolAImpl obj = new ProtocolAImpl();
// 수퍼 클래스가 인터페이스를 구현했다면,
// 그 서브 클래스는 자동으로 인터페이스를 구현한 것이 된다.
// 증명!
//
ProtocolA a = obj;
a.rule2();
}
public static void main(String[] args) {
new Exam0120().test();
}
}
- List 인터페이스의 나머지 메서드는 추상 메서드인채로 그냥 둔다.
→ 왜? 서브 클래스마다 구현하는 방법이 다르기 때문이다.
package com.bitcamp.util;
public abstract class AbstractList implements List{
// 목록에 저장된 항목의 개수를 저장하는 필드는 모든 서브 클래스가 가져야 할 필드이다.
// 또한 이 필드는 서브 클래스에서 접근 가능하도록 개방한다.
// 즉 서브 클래스가 사용할 수 있도록 물려주기 위해 만든 필드는
// 서브 클래스에서 직접 접근할 수 있게 해야 한다.
//
protected int size; // 같은 패키지에 소속된 클래스 + 서브 클래스는 직접 접근 가능!
// 서브 클래스에게 상속해 줄 메서드를 미리 구현
@Override
public int size() {
return size;
}
}
public class ObjectList extends AbstractList {
...
public class LinkedList extends AbstractList {
...
- 특정 클래스 안에서만 사용될 클래스라면 그 클래스 내부에 정의하는 것이 유지보수에 좋다.
- 클래스 안에 정의된 클래스를 중첩 클래스(nested class)라고 한다.
- 특정 인스턴스에 종속되지 않는(인스턴스 사용X) 중첩 클래스라면 static nested class( 스태틱 중첩클래스)로 정의한다.
package com.bitcamp.util;
/**
* Node를 이용해 값을 목록을 관리하는 일을 한다.
*
* @author vivi
*
*/
public class LinkedList extends AbstractList {
private Node head; // 첫 노드의 주소를 저장
private Node tail; // 마지막 노드의 주소를 저장
@Override
public void add(Object value) {
Node node = new Node(value);
size++; // 목록의 크기를 한 개 증가시킨다.
if (tail == null) {
head = tail = node; // 첫 노드를 등록한다.
return;
}
tail.next = node; // 리스트 끝에 새 노드를 연결한다.
node.prev = tail; // 새 노드가 현재의 끝 노드를 가리키게 한다.
tail = node; // 새 노드를 끝 노드로 만든다.
}
@Override
public Object get(int index) {
if (index < 0 || index >= size) {
throw new ListException("인덱스의 범위를 초과했습니다!");
}
Node cursor = head;
for (int i = 0; i < index; i++) {
cursor = cursor.next;
}
return cursor.value;
}
@Override
public Object remove(int index) {
if (index < 0 || index >= size) {
throw new ListException("인덱스의 범위를 초과했습니다!");
}
size--;
Object deleted;
if (head == tail) { // 마지막 남은 노드를 제거할 때
deleted = head.value; // 노드를 삭제하기 전에 리턴할 수 있도록 값을 임시 보관한다.
head.value = null; // 노드에 들어 있는 값 객체의 주소를 비운다.
head = tail = null;
return deleted; // 메서드를 종료할 때 호출자에게 삭제한 값을 리턴한다.
}
Node cursor = head;
for (int i = 0; i < index; i++) {
cursor = cursor.next;
}
if (cursor.prev != null) { // 맨 앞 노드가 아니라면
cursor.prev.next = cursor.next; // 현재 노드의 다음 노드 주소를 이전 노드의 next 저장
} else { // 맨 앞 노드라면
head = cursor.next; // 삭제할 다음 노드를 시작 노드로 설정한다.
head.prev = null; // 시작 노드이기에 앞노드를 가리키지 않게 한다.
}
if (cursor.next != null) { // 마지막 노드가 아니라면
cursor.next.prev = cursor.prev; // 현재 노드의 이전 노드 주소를 다음 노드의 prev 저장
} else { // 마지막 노드라면
tail = cursor.prev; // 현재 커서의 이전 노드를 마지막 노드로 설정한다.
tail.next = null; // 마지막 노드이기에 다음 노드를 가리키지 않게 한다.
}
deleted = cursor.value; // 노드를 삭제하기 전에 노드에 들어 있는 값을 임시 보관해 둔다.
cursor.value = null;
cursor.prev = null;
cursor.next = null;
return deleted; // 메서드를 리턴할 때 삭제된 값을 호출자에게 전달한다.
}
@Override
public Object[] toArray() {
// 값을 담을 배열을 준비
Object[] arr = new Object[size];
// 노드를 따라 가면서 값을 꺼내 배열에 담는다.
Node cursor = head;
for (int i = 0; i < size; i++) {
arr[i] = cursor.value;
cursor = cursor.next;
}
return arr;
}
// LinkedList에서만 사용할 클래스라면, 이 클래스 안에 선언하는 것이 유지보수에 좋다!
// 클래스 안에 정의된 클래스를 중첩 클래스, (nested class)라고 한다.
//
// 특정 인스턴스에 종속되지 않는 중첩 클래스라면 static nested class( 스태틱 중첩클래스)로 정의한다.
// LinkedList의 특정 인스턴스에 종속되지 않기 때문에 static nested class( 스태틱 중첩클래스)로 정의한다.
// 크기도 작으므로!
// 어차피 LinkedList에서만 사용하므로 넣어버리고, private으로 설정한다.
//
private static class Node {
Object value;
Node prev;
Node next;
public Node(Object v) {
this.value = v;
}
}
}// LinkedList 끝
package com.bitcamp.util;
public class Stack extends LinkedList{
public void push(Object value) {
add(value); // 수퍼 클래스의 메서드를 호출하여push() 기능을 구현한다.
}
public Object pop() {
// 수퍼 클래스의 메서드를 호출하여pop() 기능을 구현한다.
return remove(size()-1);
}
public boolean empty() {
return size() == 0;
}
public Object peek() {
return get(size()-1);
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
for(int i=0;i<size();i++) { // 스택에 저장된 개수만큼 반복한다.
if(buf.length() > 0) {
// 이미 버퍼에 저장된 문자열이 있다면,
buf.append(" > ");
}
buf.append(get(i));
}
return buf.toString();
}
}
public class App {
//breadCrumb 메뉴를 저장할 스택을 준비
public static Stack breadcrumbMenu = new Stack();
public static void main(String[] args) {
...
}
}
board.App 클래스 변경
handler.XxxHandler 클래스 변경
print계열 함수들은 자동으로 알아서 toString()을 가져와서 출력을 하는데, 이 toString()을 오버라이딩 하면,
print안에 레퍼런스 변수만 넣고 .toString()을 붙이지 않아도,
Stack class에서 출력방식을 바꾼 오버라이딩한대로, 자동으로 출력이 된다.
System.out.printf("%s\n", breadcrumbMenu);
public class App {
//breadCrumb 메뉴를 저장할 스택을 준비
public static Stack breadcrumbMenu = new Stack();
public static void main(String[] args) {
welcome();
BoardHandler boardHandler = new BoardHandler();
BoardHandler readingHandler = new BoardHandler();
BoardHandler visitHandler = new BoardHandler();
BoardHandler noticeHandler = new BoardHandler();
BoardHandler diaryHandler = new BoardHandler();
MemberHandler memberHandler = new MemberHandler();
// "메인" 메뉴의 이름을 스택에 등록한다.
breadcrumbMenu.push("메인");
// 메뉴명을 저장할 배열을 준비한다.
String [] menus = {"게시판", "독서록", "방명록", "공지사항", "일기장", "회원"};
loop: while (true) {
// 메인 메뉴 출력
System.out.printf("%s\n", breadcrumbMenu);
printMenus(menus);
try {
int mainMenuNo = Prompt.inputInt("메뉴를 선택하세요[1..6](0: 종료) ");
if(mainMenuNo > 0 && mainMenuNo <= menus.length) {
//메뉴에 진입할 때 breadCrumb메뉴바에 그 메뉴를 등록한다.
breadcrumbMenu.push(menus[mainMenuNo-1]);
} else if(mainMenuNo == 0){
break loop;
}else {
System.out.println("메뉴 번호가 옳지 않습니다!");
continue; // while문의 조건 검사로 보낸다.
}
switch (mainMenuNo) {
case 1: // 게시판
boardHandler.execute();
break;
case 2: // 독서록
readingHandler.execute();
break;
case 3: // 방명록
visitHandler.execute();
break;
case 4: // 공지사항
noticeHandler.execute();
break;
case 5: // 일기장
diaryHandler.execute();
break;
case 6: // 회원
memberHandler.execute();
break;
default:
} // switch
breadcrumbMenu.pop();
} catch (Exception ex) {
System.out.println("입력 값이 옳지 않습니다.");
}
} // while
System.out.println("안녕히 가세요!");
Prompt.close();
} // main
static void welcome() {
System.out.println("[게시판 애플리케이션]");
System.out.println();
System.out.println("환영합니다!");
System.out.println();
}
static void printMenus(String[] menus) {
for(int i=0;i<menus.length; i++) {
System.out.printf(" %d: %s\n", i + 1, menus[i]);
}
System.out.println();
}
}
public class BoardHandler {
// 게시글 목록을 관리할 객체 준비
private BoardDao boardDao = new BoardDao();
// 모든 인스턴스가 같은 서브 메뉴를 가지기 때문에
// 메뉴명을 저장할 배열은 스태틱, 클래스 필드로 준비한다.
private static String [] menus = {"목록", "상세보기", "등록", "삭제", "변경"};
static void printMenus(String[] menus) {
for(int i=0;i<menus.length; i++) {
System.out.printf(" %d: %s\n", i + 1, menus[i]);
}
System.out.println();
}
public void execute() {
while (true) {
System.out.printf("%s:\n", App.breadcrumbMenu); // printf가 알아서 toString을 호출하므로 따로 안붙여줘도 ㄱㅊ
printMenus(menus);
try {
int menuNo = Prompt.inputInt("메뉴를 선택하세요[1..5](0: 이전) ");
if(menuNo > 0 && menuNo <= menus.length) {
//메뉴에 진입할 때 breadCrumb메뉴바에 그 메뉴를 등록한다.
App.breadcrumbMenu.push(menus[menuNo-1]);
} else if(menuNo == 0){
return; // 메인 메뉴로 돌아간다.
}else {
System.out.println("메뉴 번호가 옳지 않습니다!");
continue; // while문의 조건 검사로 보낸다.
}
displayHeadline();
// 서브 메뉴의 제목을 출력한다.
System.out.printf("%s: \n",App.breadcrumbMenu);
switch (menuNo) {
case 1:
this.onList(); break;
case 2: this.onDetail(); break;
case 3: this.onInput(); break;
case 4: this.onDelete(); break;
case 5: this.onUpdate(); break;
default:
}
displayBlankLine();
App.breadcrumbMenu.pop();
} catch (Exception ex) {
System.out.printf("예외 발생: %s\n", ex.getMessage());
}
} // 게시판 while
}
- before: 스택과 큐를 설명하시오!
- 스택: LIFO, 프링글스 통
- 큐: FIFO, 휴지곽
- after: 적용 예를 들어보시오!
- 스택 → 웹 페이지 방문 기록, breadcrumb
- 큐 → 예약 처리 (먼저 예약한 사람 먼저 처리)
// 사용자 요청을 다룰 객체의 사용법을 정의한다.
//
public interface Handler {
void execute();
}
public class BoardHandler implements Handler{
...
}
public class MemberHandler implements Handler{
...
}
board.App 클래스 변경
Handler 배열 만들기
Handler[] handlers = new Handler[] { //개수 정하지 않고 배열선언한다.
new BoardHandler(), // 게시판
new BoardHandler(), // 독서록
new BoardHandler(), // 방명록
new BoardHandler(), // 공지사항
new BoardHandler(), // 일기장
new MemberHandler() // 회원
};
switch (mainMenuNo) {
case 1: // 게시판
boardHandler.execute();
break;
case 2: // 독서록
readingHandler.execute();
break;
case 3: // 방명록
visitHandler.execute();
break;
case 4: // 공지사항
noticeHandler.execute();
break;
case 5: // 일기장
diaryHandler.execute();
break;
case 6: // 회원
memberHandler.execute();
break;
default:
} // switch
↓
// 메뉴 번호로 Handler 레퍼런스에 들어있는 객체를 찾아 실행한다.
handlers[mainMenuNo-1].execute();
public class App {
//breadCrumb 메뉴를 저장할 스택을 준비
public static Stack breadcrumbMenu = new Stack();
public static void main(String[] args) {
welcome();
// 핸들러를 담을 레퍼런스 배열을 준비한다.
Handler[] handlers = new Handler[] { //개수 정하지 않고 배열선언한다.
new BoardHandler(), // 게시판
new BoardHandler(), // 독서록
new BoardHandler(), // 방명록
new BoardHandler(), // 공지사항
new BoardHandler(), // 일기장
new MemberHandler() // 회원
};
// "메인" 메뉴의 이름을 스택에 등록한다.
breadcrumbMenu.push("메인");
// 메뉴명을 저장할 배열을 준비한다.
String [] menus = {"게시판", "독서록", "방명록", "공지사항", "일기장", "회원"};
loop: while (true) {
// 메인 메뉴 출력
System.out.printf("%s\n", breadcrumbMenu);
printMenus(menus);
try {
int mainMenuNo = Prompt.inputInt("메뉴를 선택하세요[1..6](0: 종료) ");
if(mainMenuNo > 0 && mainMenuNo <= menus.length) {
//메뉴에 진입할 때 breadCrumb메뉴바에 그 메뉴를 등록한다.
breadcrumbMenu.push(menus[mainMenuNo-1]);
} else if(mainMenuNo == 0){
break loop;
}else {
System.out.println("메뉴 번호가 옳지 않습니다!");
continue; // while문의 조건 검사로 보낸다.
}
// 메뉴 번호로 Handler 레퍼런스에 들어있는 객체를 찾아 실행한다.
handlers[mainMenuNo-1].execute();
breadcrumbMenu.pop();
} catch (Exception ex) {
System.out.println("입력 값이 옳지 않습니다.");
}
} // while
System.out.println("안녕히 가세요!");
Prompt.close();
} // main
static void welcome() {
System.out.println("[게시판 애플리케이션]");
System.out.println();
System.out.println("환영합니다!");
System.out.println();
}
static void printMenus(String[] menus) {
for(int i=0;i<menus.length; i++) {
System.out.printf(" %d: %s\n", i + 1, menus[i]);
}
System.out.println();
}
}