Menu 열거형은 전체가 공유하는 글로벌 하위 탭에 상수로 존재함. (페이지는 항상 존재해야함)
public interface Controller {
void show(); // 서비스(Model)와 뷰를 연결
void run(); // 메뉴에서 선택하는 기능
}
public abstract class AbstractController implements Controller {
protected Scanner sc;
public AbstractController() {
sc = new Scanner(System.in);
}
/**
* 상단 공통 출력 부분
*/
public void common() {
System.out.println("학생관리 프로그램 Ver1.0");
System.out.println(Templates.getInstance().doubleLine());
}
/**
* 입력 항목
* - 문자: q, exit, quit - 종료
* - 숫자: 메뉴 항목
*/
public void prompt() { //메뉴를 선택함. (메뉴는 계속해서 선택하기에 글로벌에서 구현)
System.out.print("메뉴 선택: ");
String menu = sc.nextLine();
if (menu.equals("q") || menu.equals("quit") || menu.equals("exit")) {
System.out.println("종료 합니다.");
System.exit(0); // 0 - 정상 종료, 1 - 비정상 종료
}
try {
int m = Integer.parseInt(menu);
change(m); // 메뉴 변경
} catch (Exception e) {
e.printStackTrace();
System.out.println("메뉴는 숫자로 입력하세요.");
}
}
/**
* 입력과 검증을 함께 진행
*
* @param message : 항목 메세지
* @param predicate : 판별식
* @return
*/
protected String promptWithValidation(String message, Predicate<String> predicate) {
String str = null;
do {
System.out.print(message);
str = sc.nextLine();
} while(!predicate.test(str));
return str;
}
/**
* 템플릿 메서드 패턴 : 특정 절차가 고정되어 있는 경우
*
*/
@Override
public final void run() { //화면에서 정상적으로 작동시에 출력되는 순서대로 종합.
common(); //공통적으로 모든화면에서 보여줘야하는 것
show(); //화면을 보여줌
prompt(); //메뉴 선택과 관련
}
private void change(int menuNo) { //화면 변경시에 사용할 메서드
Menu menu = null;
switch(menuNo) {
case 1: menu = Menu.JOIN; break; // 회원가입
case 2: menu = Menu.LOGIN; break; // 로그인
default: menu = Menu.MAIN; // 메인 메뉴
}
// 메뉴 컨트롤러 변경 처리 - Router
MainRouter.getInstance().change(menu);
}
}
(인터페이스로 구현이지만 편의상 부모라 칭하겠음)
이 메서드에서
memlocator는 다형성을 사용해서
MemberControllerLocator의 부모인 ControllerLocator 인터페이스는
위 사진의 find라는 이름을 가진 메서드는 Controller 자료형의 객체를 반환하며 menu 열거형을 매개변수로 가진다.
find의 자료형인 Controller 인터페이스는
show()와 run() 메서드를 정의하고 있다.
한편, AbstractController는 Controller의 run 메서드를 구현하고 있다. (AbstractController가 자식임)
package org.choongang.global;
import org.choongang.global.constants.Menu;
import org.choongang.main.MainRouter;
import org.choongang.template.Templates;
import java.util.Scanner;
import java.util.function.Predicate;
public abstract class AbstractController implements Controller {
...
@Override
public final void run() {
common();
show();
prompt();
}
...
public void prompt() {
System.out.print("메뉴 선택: ");
String menu = sc.nextLine();
if (menu.equals("q") || menu.equals("quit") || menu.equals("exit")) {
System.out.println("종료 합니다.");
System.exit(0); // 0 - 정상 종료, 1 - 비정상 종료
}
try {
int m = Integer.parseInt(menu);
change(m); // 메뉴 변경
} catch (Exception e) {
e.printStackTrace();
System.out.println("메뉴는 숫자로 입력하세요.");
}
}
...
}
이제 다시 memlocator의 기능을 수행하는 MemberControllerLocator로 돌아와본다.
public class MemberControllerLocator implements ControllerLocator {
...
@Override
public Controller find(Menu menu) {
Controller controller = controllers.get(menu);
if (controller != null) {
return controller;
}
switch(menu) {
case JOIN: controller = new JoinController(); break;
default: controller = new LoginController();
}
controllers.put(menu, controller);
return controller;
}
}
해당 클래스에서 ControllerLocator의 find 메서드에서 객체를 생성하게끔 구현해놨다. (JoinController, LoginController 객체를 생성하게끔)
여기서 JoinController를 보면
public class JoinController extends AbstractController {
...
public void show() {
Templates.getInstance().render(Menu.JOIN);
}
public void prompt() {
String userId = promptWithValidation("아이디(6자리 이상): ", s -> s.length() >= 6);
String userPw = promptWithValid ....
...
}
AbstractController를 상속받고 있다.
예) JoinController에서 AbstractController의 prompt메서드를 재정의하여 사용하고 있다.
다시 원래 처음 으로 돌아오면
위 과정을 거쳐 controller는 memlocator.find 메서드를 통해 Join, LoginController 객체를 가질 수 있었고
해당 객체에서는 AbstractController를 상속받고 있기에 run메서드를 통해 화면을 구현하는 과정을 일련적으로 진행할 수 있었다.
show 메서드는
컨트롤러에서 구현했다.