mini-pms 33a
Observer 패턴 적용
01) App 클래스의 스태틱 멤버(필드와 메서드)를 인스턴트 멤버로 전환한다.
public class App {
public static void main(String[] args) throws Exception {
App01 app = new App01();
app.service();
}
// 스태틱 멤버들이 공유하는 변수가 아니라면 로컬 변수로 만든다.
public void service() throws Exception {
List<Board> boardList = new ArrayList<>();
File boardFile = new File("./board.json");
List<Member> memberList = new LinkedList<>();
File memberFile = new File("./member.json");
List<Project> projectList = new LinkedList<>();
File projectFile = new File("./project.json");
List<Task> taskList = new ArrayList<>();
File taskFile = new File("./task.json");
loadObjects(boardList, boardFile, Board[].class);
loadObjects(memberList, memberFile, Member[].class);
loadObjects(projectList, projectFile, Project[].class);
loadObjects(taskList, taskFile, Task[].class);
Map<String,Command> commandMap = new HashMap<>();
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));
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));
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));
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) {
try {
command.execute();
} catch (Exception e) {
System.out.println("--------------------------------------------------------------");
System.out.printf("명령어 실행 중 오류 발생: %s\n", e);
System.out.println("--------------------------------------------------------------");
}
} else {
System.out.println("실행할 수 없는 명령입니다.");
}
}
System.out.println();
}
Prompt.close();
saveObjects(boardList, boardFile);
saveObjects(memberList, memberFile);
saveObjects(projectList, projectFile);
saveObjects(taskList, taskFile);
}
// 스태틱 멤버들이 공유하는 변수가 아니라면 로컬 변수로 만든다.
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 명령 처리 중 오류 발생!");
}
}
// 스태틱 멤버들이 공유하는 변수가 아니라면 로컬 변수로 만든다.
private void saveObjects(Collection<?> list, File file) {
BufferedWriter out = null;
try {
out = new BufferedWriter(new FileWriter(file));
Gson gson = new Gson();
String jsonStr = gson.toJson(list);
out.write(jsonStr);
out.flush();
System.out.printf("총 %d 개의 객체를 '%s' 파일에 저장했습니다.\n",
list.size(), file.getName());
} catch (IOException e) {
System.out.printf("객체를 '%s' 파일에 쓰는 중 오류 발생! - %s\n",
file.getName(), e.getMessage());
} finally {
try {
out.close();
} catch (IOException e) {
}
}
}
// 스태틱 멤버들이 공유하는 변수가 아니라면 로컬 변수로 만든다.
private <T> void loadObjects(
Collection<T> list, // 객체를 담을 컬렉션
File file, // JSON 문자열이 저장된 파일
Class<T[]> clazz // JSON 문자열을 어떤 타입의 배열로 만들 것인지 알려주는 클래스 정보
) {
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(file));
list.addAll(Arrays.asList(new Gson().fromJson(in, clazz)));
System.out.printf("'%s' 파일에서 총 %d 개의 객체를 로딩했습니다.\n",
file.getName(), list.size());
} catch (Exception e) {
System.out.printf("'%s' 파일 읽기 중 오류 발생! - %s\n",
file.getName(), e.getMessage());
} finally {
try {
in.close();
} catch (Exception e) {
}
}
}
}
02) 애플리케이션을 시작하거나 종료할 때 실행할 옵저버의 메서드 호출 규칙을 정의한다.
- com.eomcs.context.ApplicationContextListener 생성
// 애플리케이션의 상태가 변경되었을 때 호출할 메서드 규칙을 정의한다.
// 즉 애플리케이션 상태 변경에 대해 보고를 받을 "Observer" 규칙을 정의한다.
// 보통 옵저버를 "리스너(listener)/구독자(subscriber)"라 부른다.
public interface ApplicationContextListener {
// 발행자(애플리케이션)가 애플리케이션 시작을 알리기 위해 호출하는 메서드
void contextInitialized();
// 발행자(애플리케이션)가 애플리케이션 종료를 알리기 위해 호출하는 메서드
void contextDestroyed();
}
03) 옵저버를 저장할 컬렉션 객체와 옵저버를 추가하고 제거하는 메서드를 추가한다.
public class App {
// 옵저버를 보관할 컬렉션 객체
List<ApplicationContextListener> listeners = new ArrayList<>();
// 옵저버를 등록하는 메서드
public void addApplicationContextListener(ApplicationContextListener listener) {
listeners.add(listener);
}
// 옵저버를 제거하는 메서드
public void removeApplicationContextListener(ApplicationContextListener listener) {
listeners.remove(listener);
}
public static void main(String[] args) throws Exception {
App app = new App();
app.service();
}
.
.
.
}
04) 애플리케이션의 service() 실행 전/후에 옵저버에게 통지하는 코드를 추가한다.
public class App {
List<ApplicationContextListener> listeners = new ArrayList<>();
public void addApplicationContextListener(ApplicationContextListener listener) {
listeners.add(listener);
}
public void removeApplicationContextListener(ApplicationContextListener listener) {
listeners.remove(listener);
}
// service() 실행 전에 옵저버에게 통지한다.
private void notifyApplicationContextListenerOnServiceStarted() {
// 곧 서비스를 시작할테니 준비하라고, 서비스 시작에 관심있는 각 옵저버에게 통지한다.
for (ApplicationContextListener listener : listeners) {
listener.contextInitialized();
}
}
// service() 실행 후에 옵저버에게 통지한다.
private void notifyApplicationContextListenerOnServiceStopped() {
// 서비스가 종료되었으니 마무리 작업하라고, 마무리 작업에 관심있는 각 옵저버에게 통지한다.
for (ApplicationContextListener listener : listeners) {
listener.contextDestroyed();
}
}
public static void main(String[] args) throws Exception {
App app = new App();
app.service();
}
public void service() throws Exception {
// 옵저버에게 통지한다.
notifyApplicationContextListenerOnServiceStarted();
List<Board> boardList = new ArrayList<>();
File boardFile = new File("./board.json");
List<Member> memberList = new LinkedList<>();
File memberFile = new File("./member.json");
List<Project> projectList = new LinkedList<>();
File projectFile = new File("./project.json");
List<Task> taskList = new ArrayList<>();
File taskFile = new File("./task.json");
loadObjects(boardList, boardFile, Board[].class);
loadObjects(memberList, memberFile, Member[].class);
loadObjects(projectList, projectFile, Project[].class);
loadObjects(taskList, taskFile, Task[].class);
Map<String,Command> commandMap = new HashMap<>();
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));
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));
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));
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) {
try {
command.execute();
} catch (Exception e) {
System.out.println("--------------------------------------------------------------");
System.out.printf("명령어 실행 중 오류 발생: %s\n", e);
System.out.println("--------------------------------------------------------------");
}
} else {
System.out.println("실행할 수 없는 명령입니다.");
}
}
System.out.println();
}
Prompt.close();
saveObjects(boardList, boardFile);
saveObjects(memberList, memberFile);
saveObjects(projectList, projectFile);
saveObjects(taskList, taskFile);
// 옵저버에게 통지한다.
notifyApplicationContextListenerOnServiceStopped();
}
.
.
.
}
05) 애플리케이션을 시작하고 종료할 때 간단한 안내 메시지를 출력하는 옵저버를 추가한다.
- com.eomcs.pms.listener.AppInitListener 클래스 생성
public class AppInitListener implements ApplicationContextListener {
@Override
public void contextInitialized() {
System.out.println("프로젝트 관리 시스템(PMS)에 오신 걸 환영합니다!");
}
@Override
public void contextDestroyed() {
System.out.println("프로젝트 관리 시스템(PMS)을 종료합니다!");
}
}
- service() 호출 전에 옵저버를 등록한다.
public class App04 { . . . public static void main(String[] args) throws Exception { App04 app = new App04(); // 안내 메시지를 출력하는 옵저버 등록 app.addApplicationContextListener(new AppInitListener()); app.service(); } . . . }