mini-pms 33b

4 minute read

Observer 객체를 통하여 파일 다루기

  • ApplicationContextListener 인터페이스 변경
// 애플리케이션의 상태가 변경되었을 때
// 호출할 메서드 규칙을 정의한다.
// 즉 애플리케이션 상태 변경에 대해 보고를 받을 "Observer" 규칙을 정의한다.
// 보통 옵저버를 "리스너(listener)/구독자(subscriber)"라 부른다.
//
public interface ApplicationContextListener {
  // 발행자(애플리케이션)가 애플리케이션 시작을 알리기 위해 호출하는 메서드
  // - 발행자와 옵저버 간에 데이터를 공유하기 위해 맵 파라미터 추가
  // - 호출자가 옵저버의 실행 결과를 받을 수 있도록 파라미터로 맵 객체를 전달할 것이다.
  // - 리턴 값으로 결과를 전달하지 않고 파라미터로 넘어온 저장소에 보관하는 방법을 사용한다.
  // - 왜 이런 방식을 사용하는가?
  // - 파라미터 방식은 메서드에게 작업에 필요한 정보를 전달할 수 있기 때문이다.
  // - Map을 사용하여 파라미터로 전달하는 방식은 데이터의 In/Out이 가능하다.
  void contextInitialized(Map<String,Object> context);

  // 발행자(애플리케이션)가 애플리케이션 종료를 알리기 위해 호출하는 메서드
  void contextDestroyed(Map<String,Object> context);
}
  • AppInitListener 클래스 변경
public class AppInitListener implements ApplicationContextListener {
  @Override
  // 변경된 규칙에 따라 파라미터 추가
  public void contextInitialized(Map<String,Object> context) {
    System.out.println("프로젝트 관리 시스템(PMS)에 오신 걸 환영합니다!");
  }

  @Override
  // 변경된 규칙에 따라 파라미터 추가
  public void contextDestroyed(Map<String,Object> context) {
    System.out.println("프로젝트 관리 시스템(PMS)을 종료합니다!");
  }
}
  • App 변경
public class App {

  Map<String,Object> context = new Hashtable<>();

  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(context);
    }
  }

  // service() 실행 후에 옵저버에게 통지한다.
  private void notifyApplicationContextListenerOnServiceStopped() {
    for (ApplicationContextListener listener : listeners) {
      // 서비스가 종료되었으니 마무리 작업하라고,
      // 마무리 작업에 관심있는 각 옵저버에게 통지한다.
      // => 옵저버에게 맵 객체를 넘겨준다.
      // => 옵저버는 작업 결과를 파라미터로 넘겨준 맵 객체에 담아 줄 것이다.
      listener.contextDestroyed(context);
    }
  }


  public static void main(String[] args) throws Exception {
    App app = new App();

    app.addApplicationContextListener(new AppInitListener());

    app.service();
  }

  @SuppressWarnings("unchecked")
  public void service() throws Exception {

    notifyApplicationContextListenerOnServiceStarted();

    List<Board> boardList = (List<Board>) context.get("boardList");
    List<Member> memberList = (List<Member>) context.get("memberList");
    List<Project> projectList = (List<Project>) context.get("projectList");
    List<Task> taskList = (List<Task>) context.get("taskList");

    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();

    notifyApplicationContextListenerOnServiceStopped();
  }
.
.
.
}

02) 파일에서 데이터를 로딩하고 저장하는 기능을 옵저버로 옮긴다.

  • DataHandlerListener 클래스 생성
    // 게시물, 회원, 프로젝트, 작업 데이터를 파일에서 로딩하고 파일로 저장하는 일을 한다.
    public class DataHandlerListener implements ApplicationContextListener {
    
    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"); // 작업을 저장할 파일 정보
    
    @Override
    public void contextInitialized(Map<String,Object> context) {
      // 애플리케이션의 서비스가 시작되면 먼저 파일에서 데이터를 로딩한다.
      // 파일에서 데이터 로딩
      loadData(boardList, boardFile, Board[].class);
      loadData(memberList, memberFile, Member[].class);
      loadData(projectList, projectFile, Project[].class);
      loadData(taskList, taskFile, Task[].class);
    
      // 옵저버가 파일에서 데이터(게시글,회원,프로젝트,작업)를 읽어
      // List 컬렉션에 저장한 다음,
      // 발행자(App 객체)가 사용할 수 있도록 맵 객체에 담아서 공유한다.
      context.put("boardList", boardList);
      context.put("memberList", memberList);
      context.put("projectList", projectList);
      context.put("taskList", taskList);
    }
    
    @Override
    public void contextDestroyed(Map<String,Object> context) {
      // 애플리케이션 서비스가 종료되면 컬렉션에 보관된 객체를 파일에 저장한다.
      // 데이터를 파일에 저장
      saveData(boardList, boardFile);
      saveData(memberList, memberFile);
      saveData(projectList, projectFile);
      saveData(taskList, taskFile);
    }
    
    private <T> void loadData(
        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) {
        }
      }
    }
    
    private void saveData(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) {
        }
      }
    }
    }
    
  • App에 DataHandlerListener 옵저버를 등록한다.
    public class App {
    .
    .
    .  
    public static void main(String[] args) throws Exception {
      App app = new App();
    
      app.addApplicationContextListener(new AppInitListener());
      // 옵저버 등록
      app.addApplicationContextListener(new DataHandlerListener());
    
      app.service();
    }
    .
    .
    .
    }
    

Categories:

Updated: