* 한 클래스가 다른 클래스를 사용하는 것은 좋으나 두 클래스가 서로를 사용하면 안된다. 한쪽이 컴파일되면 다른 쪽이 컴파일되고 그쪽이 컴파일 되는 순간 다시 사용되는 쪽이 컴파일되기 때문에 무한 컴파일이 일어나기 때문이다. 물론 요즘은 컴파일러가 똑똑해서 무한으로 일어나지는 않는다.
실습 - 스태틱 멤버의 한계 / 인스턴스 멤버 활용
실습 목표: BoardHandler 클래스 만들고, board 클래스와, add() 메서드, list() 메서드 구현한다.
그리고 각 게시판과 멤버, 작업, 프로젝트의 종류를 여러개를 만든다.
list1 -> /board1/add -> /board1/list
list2 -> /board2/add -> /boaord2/list
list3 -> /board3/add -> /board3/list
.
.
.
방법 1) 스태틱 필드와 메서드를 하나씩 늘려라
package com.eomcs.pms.handler;
import java.util.Date;
import com.eomcs.util.Prompt;
public class BoardHandler {
static class Board {
int no;
String title;
String content;
String writer;
Date registeredDate;
int viewCount;
}
static final int LENGTH = 100;
static Board[] list = new Board[LENGTH]; // list로 이름을 바꾼다.
static int size = 0;
static Board[] list2 = new Board[LENGTH]; // list로 이름을 바꾼다.
static int size2 = 0;
public static void add() {
System.out.println("[새 게시글 등록]");
Board board = new Board();
board.no = Prompt.inputInt("번호? ");
board.title = Prompt.inputString("제목? ");
board.content = Prompt.inputString("내용? ");
while (true) {
String name = Prompt.inputString("작성자?(종료하려면 빈 문자열을 입력) ");
if (name.length() == 0) {
System.out.println("등록을 취소합니다.");
return;
}
if (MemberHandler.findByName(name) != null) {
board.writer = name;
break;
}
System.out.println("등록된 회원이 아닙니다.");
}
board.registeredDate = new java.sql.Date(System.currentTimeMillis());
System.out.println("게시글을 등록하였습니다.");
board.viewCount = 0;
list[size++] = board;
}
public static void list() {
System.out.println("[게시글 목록]");
for (int i = 0; i < size; i++) {
Board board = list[i];
System.out.printf("%d, %s, %s, %s, %d\n",
board.no,
board.title,
board.writer,
board.registeredDate,
board.viewCount);
}
}
public static void add2() {
System.out.println("[새 게시글 등록]");
Board board = new Board();
board.no = Prompt.inputInt("번호? ");
board.title = Prompt.inputString("제목? ");
board.content = Prompt.inputString("내용? ");
while (true) {
String name = Prompt.inputString("작성자?(종료하려면 빈 문자열을 입력) ");
if (name.length() == 0) {
System.out.println("등록을 취소합니다.");
return;
}
if (MemberHandler.findByName(name) != null) {
board.writer = name;
break;
}
System.out.println("등록된 회원이 아닙니다.");
}
board.registeredDate = new java.sql.Date(System.currentTimeMillis());
System.out.println("게시글을 등록하였습니다.");
board.viewCount = 0;
list2[size2++] = board;
}
public static void list2() {
System.out.println("[게시글 목록]");
for (int i = 0; i < size2; i++) {
Board board = list2[i];
System.out.printf("%d, %s, %s, %s, %d\n",
board.no,
board.title,
board.writer,
board.registeredDate,
board.viewCount);
}
}
}
=> boardHandler 코드가 무한정 늘어난다.
방법 2) BoardHandler2,3,4,5... 처럼 클래스를 늘려라
package com.eomcs.pms;
import com.eomcs.pms.handler.BoardHandler;
import com.eomcs.pms.handler.BoardHandler2;
import com.eomcs.pms.handler.BoardHandler3;
import com.eomcs.pms.handler.BoardHandler4;
import com.eomcs.pms.handler.BoardHandler5;
import com.eomcs.pms.handler.BoardHandler6;
import com.eomcs.pms.handler.MemberHandler;
import com.eomcs.pms.handler.ProjectHandler;
import com.eomcs.pms.handler.TaskHandler;
import com.eomcs.util.Prompt;
// 1) `Prompt` 클래스를 별도의 패키지로 분류한다
// 2) 핸들러 클래스들을 별도의 패키지로 분류한다
public class App {
public static void main(String[] args) {
loop:
while (true) {
String command = Prompt.inputString("명령> ");
switch (command) {
case "/board/add": BoardHandler.add(); break;
case "/board/list": BoardHandler.list(); break;
case "/board2/add": BoardHandler2.add(); break;
case "/board2/list": BoardHandler2.list(); break;
case "/board3/add": BoardHandler3.add(); break;
case "/board3/list": BoardHandler3.list(); break;
case "/board4/add": BoardHandler4.add(); break;
case "/board4/list": BoardHandler4.list(); break;
case "/board5/add": BoardHandler5.add(); break;
case "/board5/list": BoardHandler5.list(); break;
case "/board6/add": BoardHandler6.add(); break;
case "/board6/list": BoardHandler6.list(); break;
case "/member/add": MemberHandler.add(); break;
case "/member/list": MemberHandler.list(); break;
case "/project/add": ProjectHandler.add(); break;
case "/project/list": ProjectHandler.list(); break;
case "/task/add": TaskHandler.add(); break;
case "/task/list": TaskHandler.list(); break;
case "quit":
case "exit": System.out.println("안녕!"); break loop;
default: System.out.println("실행할 수 없는 명령입니다.");
}
System.out.println(); // 이전 명령의 실행을 구분하기 위해 빈 줄 출력
}
Prompt.close();
}
}
=> 게시판 삭제가 불가능하고, 한 클래스에서 버그가 나오면 모든 클래스를 수정해야하므로 유지보수가 어렵다.
방법 3) BoardHandler의 멤버들 중에 개별적으로 다룰 멤버들은 인스턴스 멤버로 바꿔 인스턴스별로 다룰 수 있게 하라.
핸들러 클래스의 멤버들을 (도메인 클래스들을 제외하고) 인스턴스 멤버로 전환한다. 그리고 App 클래스에서 각 핸들러 클래스들의 인스턴스를 생성하여 다룬다. MemberHandler의 findByName() 메서드를 인스턴스 메서드로 변경하면 이를 다른 핸들러 클래스들이 MemberHandler 클래스를 의존 객체로 사용해야한다. 해당 클래스들에서 직접 App클래스에 있는 MemberHandler 인스턴스를 참조하면 각 핸들러 클래스들과 App 클래스가 크로스 참조 되는 상황에 빠진다. 따라서 이를 피할 수 있도록 각 핸들러 클래스에서 Memberhandler 타입의 필드를 갖고 이에 집어넣을 인스턴스를 App 클래스에서 직접 주입해준다.
package com.eomcs.pms.handler;
import java.util.Date;
import com.eomcs.util.Prompt;
public class BoardHandler {
static class Board {
int no;
String title;
String content;
String writer;
Date registeredDate;
int viewCount;
}
// 공통으로 사용할 값을 보관하는 변수는 스태틱 멤버(클래스 멤버)로 만든다.
static final int LENGTH = 100;
// 개별적으로 관리되어야할 변수는 인스턴스 멤버(non-static 멤버)로 만든다.
Board[] list = new Board[LENGTH];
int size = 0;
MemberHandler memberHandler;
public void add() {
System.out.println("[게시물 등록]");
Board board = new Board();
board.no = Prompt.inputInt("번호? ");
board.title = Prompt.inputString("제목? ");
board.content = Prompt.inputString("내용? ");
while (true) {
String name = Prompt.inputString("작성자?(종료하려면 빈 문자열을 입력) ");
if (name.length() == 0) {
System.out.println("등록을 취소합니다.");
return;
}
if (memberHandler.findByName(name) != null) {
board.writer = name;
break;
}
System.out.println("등록된 회원이 아닙니다.");
}
board.registeredDate = new java.sql.Date(System.currentTimeMillis());
System.out.println("게시글을 등록하였습니다.");
board.viewCount = 0;
list[size++] = board;
}
public void list() {
System.out.println("[게시글 목록]");
for (int i = 0; i < size; i++) {
Board board = list[i];
System.out.printf("%d, %s, %s, %s, %d\n",
board.no,
board.title,
board.writer,
board.registeredDate,
board.viewCount);
}
}
}
package com.eomcs.pms.handler;
import java.sql.Date;
import com.eomcs.util.Prompt;
public class MemberHandler {
// 회원 데이터
static class Member {
int no;
String name;
String email;
String password;
String photo;
String tel;
Date registeredDate;
}
static final int LENGTH = 100;
Member[] list = new Member[LENGTH]; // list로 이름을 바꾼다.
int size = 0;
// 다른 패키지에서 이 메서드를 사용할 수 있도록 public 으로 사용 범위를 공개한다.
public void add() {
System.out.println("[회원 등록]");
Member member = new Member();
member.no = Prompt.inputInt("번호? ");
member.name = Prompt.inputString("이름? ");
member.email = Prompt.inputString("이메일? ");
member.password = Prompt.inputString("암호? ");
member.photo = Prompt.inputString("사진? ");
member.tel = Prompt.inputString("전화? ");
member.registeredDate = new java.sql.Date(System.currentTimeMillis());
list[size++] = member;
}
public void list() {
System.out.println("[회원 목록]");
for (int i = 0; i < this.size; i++) {
Member member = list[i];
System.out.printf("%d, %s, %s, %s, %s\n",
member.no,
member.name,
member.email,
member.tel,
member.registeredDate);
}
}
public String findByName(String name) {
for (int i = 0; i < this.size; i++) {
if (this.list[i].name.equals(name)) {
return name;
}
}
return null;
}
}
package com.eomcs.pms.handler;
import java.sql.Date;
import com.eomcs.util.Prompt;
public class ProjectHandler {
// 프로젝트 데이터
static class Project {
int no;
String title;
String content;
Date startDate;
Date endDate;
String owner;
String members;
}
final int LENGTH = 100; // PLENGTH 를 LENGTH 로 변경한다.
Project[] list = new Project[LENGTH]; // projects 를 list 로 변경한다.
int size = 0; // psize 를 size 로 변경한다.
MemberHandler memberHandler;
//다른 패키지에서 이 메서드를 사용할 수 있도록 public 으로 사용 범위를 공개한다.
public void add() {
System.out.println("[프로젝트 등록]");
Project project = new Project();
project.no = Prompt.inputInt("번호? ");
project.title = Prompt.inputString("프로젝트명? ");
project.content = Prompt.inputString("내용? ");
project.startDate = Prompt.inputDate("시작일? ");
project.endDate = Prompt.inputDate("종료일? ");
while (true) {
String name = Prompt.inputString("만든이?(종료하려면 빈 문자열을 입력) ");
if (name.length() == 0) {
System.out.println("등록을 취소합니다.");
return;
}
if (memberHandler.findByName(name) != null) {
project.owner = name;
break;
}
System.out.println("등록된 회원이 아닙니다.");
}
StringBuilder names = new StringBuilder();
while (true) {
String name = Prompt.inputString("팀원?(종료하려면 빈 문자열을 입력) ");
if (name.length() == 0) {
break;
}
if (memberHandler.findByName(name) != null) {
names.append(name + ",");
project.members = names.toString();
} else {
System.out.println("등록된 회원이 아닙니다.");
}
}
this.list[this.size++] = project;
}
public void list() {
System.out.println("[프로젝트 목록]");
for (int i = 0; i < this.size; i++) {
Project project = this.list[i];
System.out.printf("%d, %s, %s, %s, %s, [%s]\n",
project.no,
project.title,
project.startDate,
project.endDate,
project.owner,
project.members);
}
}
}
package com.eomcs.pms.handler;
import java.sql.Date;
import com.eomcs.util.Prompt;
public class TaskHandler {
// 작업 데이터
static class Task {
int no;
String content;
Date deadline;
int status;
String owner;
}
final int LENGTH = 100; // TLENGTH 를 LENGTH 로 변경한다.
Task[] list = new Task[LENGTH]; // tasks 를 list 로 변경한다.
int size = 0; // tsize 를 size 로 변경한다.
MemberHandler memberHandler;
//다른 패키지에서 이 메서드를 사용할 수 있도록 public 으로 사용 범위를 공개한다.
public void add() {
System.out.println("[작업 등록]");
Task task = new Task();
task.no = Prompt.inputInt("번호? ");
task.content = Prompt.inputString("내용? ");
task.deadline = Prompt.inputDate("마감일? ");
task.status = Prompt.inputInt("상태?\n0: 신규\n1: 진행중\n2: 완료\n> ");
while (true) {
String name = Prompt.inputString("담당자?(종료하려면 빈 문자열을 입력) ");
if (name.length() == 0) {
System.out.println("등록을 취소합니다.");
return;
}
if (memberHandler.findByName(name) != null) {
task.owner = name;
break;
}
System.out.println("등록된 회원이 아닙니다.");
}
list[size++] = task;
}
public void list() {
System.out.println("[작업 목록]");
for (int i = 0; i < size; i++) {
Task task = list[i];
String stateLabel = null;
switch (task.status) {
case 1:
stateLabel = "진행중";
break;
case 2:
stateLabel = "완료";
break;
default:
stateLabel = "신규";
}
System.out.printf("%d, %s, %s, %s, %s\n",
task.no,
task.content,
task.deadline,
stateLabel,
task.owner);
}
}
}
package com.eomcs.pms;
import com.eomcs.pms.handler.BoardHandler;
import com.eomcs.pms.handler.MemberHandler;
import com.eomcs.pms.handler.ProjectHandler;
import com.eomcs.pms.handler.TaskHandler;
import com.eomcs.util.Prompt;
// 1) `Prompt` 클래스를 별도의 패키지로 분류한다
// 2) 핸들러 클래스들을 별도의 패키지로 분류한다
public class App {
public static void main(String[] args) {
MemberHandler memberHandler = new MemberHandler();
BoardHandler boardHandler = new BoardHandler();
boardHandler.memberHandler = memberHandler;
ProjectHandler projectHandler = new ProjectHandler();
projectHandler.memberHandler = memberHandler;
TaskHandler taskHandler = new TaskHandler();
taskHandler.memberHandler = memberHandler;
loop:
while (true) {
String command = Prompt.inputString("명령> ");
switch (command) {
case "/board/add": boardHandler.add(); break;
case "/board/list": boardHandler.list(); break;
case "/member/add": memberHandler.add(); break;
case "/member/list": memberHandler.list(); break;
case "/project/add": projectHandler.add(); break;
case "/project/list": projectHandler.list(); break;
case "/task/add": taskHandler.add(); break;
case "/task/list": taskHandler.list(); break;
case "quit":
case "exit": System.out.println("안녕!"); break loop;
default: System.out.println("실행할 수 없는 명령입니다.");
}
System.out.println(); // 이전 명령의 실행을 구분하기 위해 빈 줄 출력
}
Prompt.close();
}
}
* 실무에서는 확장성을 위해서(클래스의 인스턴스를 생성하여 다룰 것을 대비하여) 일부러 처음부터 스태틱 멤버보다는 인스턴스 멤버를 생성한다.
실습 2 - 생성자의 필요성
실습 목표 : 생성자를 통해 의존 객체를 강제로 주입하게 만든다.
ProjectHandler와 TaskHandler, BoardHandler의 생성자를 추가한다.
의존 객체가 필요한 상황에서 생성자를 사용하지 않으면 다음과 같이 나중에 의존 객체 주입을 하는 것을 잊을 수도 있다. 컴파일 까지는 문제 없기 때문에 오히려 더 알기 힘들다.
package com.eomcs.pms;
import com.eomcs.pms.handler.BoardHandler;
import com.eomcs.pms.handler.MemberHandler;
import com.eomcs.pms.handler.ProjectHandler;
import com.eomcs.pms.handler.TaskHandler;
import com.eomcs.util.Prompt;
// 1) `Prompt` 클래스를 별도의 패키지로 분류한다
// 2) 핸들러 클래스들을 별도의 패키지로 분류한다
public class App {
public static void main(String[] args) {
MemberHandler memberHandler = new MemberHandler();
BoardHandler boardHandler = new BoardHandler();
// boardHandler.memberHandler = memberHandler;
ProjectHandler projectHandler = new ProjectHandler();
// projectHandler.memberHandler = memberHandler;
TaskHandler taskHandler = new TaskHandler();
// taskHandler.memberHandler = memberHandler;
loop:
while (true) {
String command = Prompt.inputString("명령> ");
switch (command) {
case "/board/add": boardHandler.add(); break;
case "/board/list": boardHandler.list(); break;
case "/member/add": memberHandler.add(); break;
case "/member/list": memberHandler.list(); break;
case "/project/add": projectHandler.add(); break;
case "/project/list": projectHandler.list(); break;
case "/task/add": taskHandler.add(); break;
case "/task/list": taskHandler.list(); break;
case "quit":
case "exit": System.out.println("안녕!"); break loop;
default: System.out.println("실행할 수 없는 명령입니다.");
}
System.out.println(); // 이전 명령의 실행을 구분하기 위해 빈 줄 출력
}
Prompt.close();
}
}
따라서 생성자를 통해서 의존 객체 주입 없이는 생성하지 못하게 하는 것이다. 파라미터로 MemberHandler의 객체를 받는 생성자를 지정하여 강제로 의존 객체를 주입할 수 있도록 한다.
package com.eomcs.pms.handler;
import java.util.Date;
import com.eomcs.util.Prompt;
public class BoardHandler {
static class Board {
int no;
String title;
String content;
String writer;
Date registeredDate;
int viewCount;
}
// 공통으로 사용할 값을 보관하는 변수는 스태틱 멤버(클래스 멤버)로 만든다.
static final int LENGTH = 100;
// 개별적으로 관리되어야할 변수는 인스턴스 멤버(non-static 멤버)로 만든다.
Board[] list = new Board[LENGTH];
int size = 0;
MemberHandler memberHandler;
public BoardHandler(MemberHandler memberHandler) {
this.memberHandler = memberHandler;
}
public void add() {
System.out.println("[게시물 등록]");
Board board = new Board();
board.no = Prompt.inputInt("번호? ");
board.title = Prompt.inputString("제목? ");
board.content = Prompt.inputString("내용? ");
while (true) {
String name = Prompt.inputString("작성자?(종료하려면 빈 문자열을 입력) ");
if (name.length() == 0) {
System.out.println("등록을 취소합니다.");
return;
}
if (memberHandler.findByName(name) != null) {
board.writer = name;
break;
}
System.out.println("등록된 회원이 아닙니다.");
}
board.registeredDate = new java.sql.Date(System.currentTimeMillis());
System.out.println("게시글을 등록하였습니다.");
board.viewCount = 0;
list[size++] = board;
}
public void list() {
System.out.println("[게시글 목록]");
for (int i = 0; i < size; i++) {
Board board = list[i];
System.out.printf("%d, %s, %s, %s, %d\n",
board.no,
board.title,
board.writer,
board.registeredDate,
board.viewCount);
}
}
}
package com.eomcs.pms.handler;
import java.sql.Date;
import com.eomcs.util.Prompt;
public class ProjectHandler {
// 프로젝트 데이터
static class Project {
int no;
String title;
String content;
Date startDate;
Date endDate;
String owner;
String members;
}
final int LENGTH = 100; // PLENGTH 를 LENGTH 로 변경한다.
Project[] list = new Project[LENGTH]; // projects 를 list 로 변경한다.
int size = 0; // psize 를 size 로 변경한다.
MemberHandler memberHandler;
public ProjectHandler(MemberHandler mamberHandler) {
this.memberHandler = memberHandler;
}
//다른 패키지에서 이 메서드를 사용할 수 있도록 public 으로 사용 범위를 공개한다.
public void add() {
System.out.println("[프로젝트 등록]");
Project project = new Project();
project.no = Prompt.inputInt("번호? ");
project.title = Prompt.inputString("프로젝트명? ");
project.content = Prompt.inputString("내용? ");
project.startDate = Prompt.inputDate("시작일? ");
project.endDate = Prompt.inputDate("종료일? ");
while (true) {
String name = Prompt.inputString("만든이?(종료하려면 빈 문자열을 입력) ");
if (name.length() == 0) {
System.out.println("등록을 취소합니다.");
return;
}
if (memberHandler.findByName(name) != null) {
project.owner = name;
break;
}
System.out.println("등록된 회원이 아닙니다.");
}
StringBuilder names = new StringBuilder();
while (true) {
String name = Prompt.inputString("팀원?(종료하려면 빈 문자열을 입력) ");
if (name.length() == 0) {
break;
}
if (memberHandler.findByName(name) != null) {
names.append(name + ",");
project.members = names.toString();
} else {
System.out.println("등록된 회원이 아닙니다.");
}
}
this.list[this.size++] = project;
}
public void list() {
System.out.println("[프로젝트 목록]");
for (int i = 0; i < this.size; i++) {
Project project = this.list[i];
System.out.printf("%d, %s, %s, %s, %s, [%s]\n",
project.no,
project.title,
project.startDate,
project.endDate,
project.owner,
project.members);
}
}
}
package com.eomcs.pms.handler;
import java.sql.Date;
import com.eomcs.util.Prompt;
public class TaskHandler {
// 작업 데이터
static class Task {
int no;
String content;
Date deadline;
int status;
String owner;
}
final int LENGTH = 100; // TLENGTH 를 LENGTH 로 변경한다.
Task[] list = new Task[LENGTH]; // tasks 를 list 로 변경한다.
int size = 0; // tsize 를 size 로 변경한다.
MemberHandler memberHandler;
public TaskHandler(MemberHandler memberHandler) {
this.memberHandler = memberHandler;
}
//다른 패키지에서 이 메서드를 사용할 수 있도록 public 으로 사용 범위를 공개한다.
public void add() {
System.out.println("[작업 등록]");
Task task = new Task();
task.no = Prompt.inputInt("번호? ");
task.content = Prompt.inputString("내용? ");
task.deadline = Prompt.inputDate("마감일? ");
task.status = Prompt.inputInt("상태?\n0: 신규\n1: 진행중\n2: 완료\n> ");
while (true) {
String name = Prompt.inputString("담당자?(종료하려면 빈 문자열을 입력) ");
if (name.length() == 0) {
System.out.println("등록을 취소합니다.");
return;
}
if (memberHandler.findByName(name) != null) {
task.owner = name;
break;
}
System.out.println("등록된 회원이 아닙니다.");
}
list[size++] = task;
}
public void list() {
System.out.println("[작업 목록]");
for (int i = 0; i < size; i++) {
Task task = list[i];
String stateLabel = null;
switch (task.status) {
case 1:
stateLabel = "진행중";
break;
case 2:
stateLabel = "완료";
break;
default:
stateLabel = "신규";
}
System.out.printf("%d, %s, %s, %s, %s\n",
task.no,
task.content,
task.deadline,
stateLabel,
task.owner);
}
}
}
package com.eomcs.pms;
import com.eomcs.pms.handler.BoardHandler;
import com.eomcs.pms.handler.MemberHandler;
import com.eomcs.pms.handler.ProjectHandler;
import com.eomcs.pms.handler.TaskHandler;
import com.eomcs.util.Prompt;
public class App {
public static void main(String[] args) {
MemberHandler memberHandler = new MemberHandler();
// 다음과 같이 기본 생성자만 있는 경우는,
// 해당 인스턴스를 생성하고 초기화시킬 때
// 외부에서 값을 받을 필요가 없다는 의미이다.
BoardHandler boardHandler = new BoardHandler(memberHandler);
// ProjectHandler 를 생성할 때 필요로 하는 의존 객체를 전달한다.
// => 이렇게 생성자를 명확하게 지정하면
// 객체를 생성하는 개발자에게 의존 객체 주입을 강제하는 효과가 있다.
ProjectHandler projectHandler = new ProjectHandler(memberHandler);
TaskHandler taskHandler = new TaskHandler(memberHandler);
loop:
while (true) {
String command = Prompt.inputString("명령> ");
switch (command) {
case "/board/add": boardHandler.add(); break;
case "/board/list": boardHandler.list(); break;
case "/member/add": memberHandler.add(); break;
case "/member/list": memberHandler.list(); break;
case "/project/add": projectHandler.add(); break;
case "/project/list": projectHandler.list(); break;
case "/task/add": taskHandler.add(); break;
case "/task/list": taskHandler.list(); break;
case "quit":
case "exit": System.out.println("안녕!"); break loop;
default: System.out.println("실행할 수 없는 명령입니다.");
}
System.out.println(); // 이전 명령의 실행을 구분하기 위해 빈 줄 출력
}
Prompt.close();
}
}
실습3 - 응집력 강화 : UI와 데이터 처리 코드 분리하기
실습 목표 : 핸들러 클래스의 두 기능을 세분화하여 여러개의 클래스로 나누어 구현한다.
이를 통해 클래스들의 응집력을 강화한다.
-
사용자의 입력을 받는 기능(UI) -> MemberHandler, ProjectHandler, BoardHandler, TaskHandler
-
데이터를 처리하는 기능(DAO- Data Access Object) -> MemberList, ProjectList, BoardList, TaskList
Low Counpling & High Cohesion
- 관계도 약화(low coupling)
한 클래스가 많은 클래스에 의존하는 구조로 작성하면 의존 클래스가 변경될 때마다 영향을 받기 때문에 유지 보수에 좋지 않다.
그래서 가능한 의존하는 클래스의 접점을 줄이는 구조로 만드는 것이 좋다.
이렇게 하면 의존 클래스의 변경에 둔감해지며 이에 따라 변경할 코드가 줄어 유지보수에 용이하다. - 응집력 강화(High Cohesion)
한 클래스가 너무 다양한 역할을 수행하면 클래스의 코드가 커지고, 변경 사항이 잦아지기 때문에 유지보수에 좋지 않다.
따라서 가능한 한 클래스가 한 개의 역할만 수행하게 만드는 것이 유지보수에 좋다.
이를 통해서 해당 클래스를 다른 프로젝트에서 재사용하거나 클래스를 교체하기가 쉽다.
1단계 : BoardHandler에서 데이터 처리 코드를 분리한다. BoardHandler에서 데이터 처리 코드를 분리하여 'BoardList' 클래스로 정의한다. BoardList의 기본 생성자와 배열의 초기 크기를 설정하는 생성자를 정의한다. 그리고 Board객체를 등록하는 add()와 데이터 목록을 리턴하는 toArray() 메서드를 정의한다. BoardHandler은 BoardList를 사용하여 입출력을 처리한다.
package com.eomcs.pms.handler;
import java.sql.Date;
public class BoardList {
static class Board {
int no;
String title;
String content;
String writer;
Date registeredDate;
int viewCount;
}
static final int DEFAULT_SIZE = 100;
Board[] list = new Board[DEFAULT_SIZE]; // list로 이름을 바꾼다.
int size = 0;
public BoardList() {
this(DEFAULT_SIZE);
}
public BoardList(int size) {
if (size < DEFAULT_SIZE) {
this.list = new Board[DEFAULT_SIZE];
} else {
this.list = new Board[size];
}
}
public void add(Board board) {
this.list[size++] = board;
}
public Board[] toArray() {
Board[] arr = new Board[size];
for (int i = 0; i < this.size; i++) {
arr[i] = list[i];
}
return arr;
}
}
package com.eomcs.pms.handler;
import com.eomcs.pms.handler.BoardList.Board;
import com.eomcs.util.Prompt;
public class BoardHandler {
BoardList boardList = new BoardList();
MemberHandler memberHandler;
public BoardHandler(MemberHandler memberHandler) {
this.memberHandler = memberHandler;
}
public void add() {
System.out.println("[게시물 등록]");
Board board = new Board();
board.no = Prompt.inputInt("번호? ");
board.title = Prompt.inputString("제목? ");
board.content = Prompt.inputString("내용? ");
while (true) {
String name = Prompt.inputString("작성자?(취소: 빈 문자열) ");
if (name.length() == 0) {
System.out.println("프로젝트 등록을 취소합니다.");
return;
} else if (memberHandler.findByName(name) != null) {
board.writer = name;
break;
}
System.out.println("등록된 회원이 아닙니다.");
}
board.registeredDate = new java.sql.Date(System.currentTimeMillis());
boardList.add(board);
}
public void list() {
System.out.println("[게시물 목록]");
Board[] boards = boardList.toArray();
for (Board board : boards) {
System.out.printf("%d, %s, %s, %s, %s\n",
board.no,
board.title,
board.writer,
board.registeredDate,
board.viewCount);
}
}
}
2단계 : Board 클래스를 패키지 멤버 클래스로 전환한다. BoardHandler와 BoardList가 둘 다 Board 클래스를 사용하므로 중첩 클래스보다 패키지 멤버 클래스로 정의하면 관리하기 쉽기 때문이다. com.eomcs.pms.domain 패키지를 새로 생성하여 넣어준다. Member, Board 처럼 데이터 타입 역할을 하는 클래스를 도메인 클래스라 부른다. 패키지 멤버 클래스는 static이 될 수 없으므로 static 을 떼고 다른 패키지에 있는 클래스가 사용해야하기 때문에 public을 붙인다.
package com.eomcs.pms.domain;
import java.sql.Date;
public class Board {
public int no;
public String title;
public String content;
public String writer;
public Date registeredDate;
public int viewCount;
}
package com.eomcs.pms.handler;
import com.eomcs.pms.domain.Board;
public class BoardList {
static final int DEFAULT_SIZE = 100;
Board[] list = new Board[DEFAULT_SIZE]; // list로 이름을 바꾼다.
int size = 0;
public BoardList() {
this(DEFAULT_SIZE);
}
public BoardList(int size) {
if (size < DEFAULT_SIZE) {
this.list = new Board[DEFAULT_SIZE];
} else {
this.list = new Board[size];
}
}
public void add(Board board) {
this.list[size++] = board;
}
public Board[] toArray() {
Board[] arr = new Board[size];
for (int i = 0; i < this.size; i++) {
arr[i] = list[i];
}
return arr;
}
}
package com.eomcs.pms.handler;
import com.eomcs.pms.domain.Board;
import com.eomcs.util.Prompt;
public class BoardHandler {
BoardList boardList = new BoardList();
MemberHandler memberHandler;
public BoardHandler(MemberHandler memberHandler) {
this.memberHandler = memberHandler;
}
public void add() {
System.out.println("[게시물 등록]");
Board board = new Board();
board.no = Prompt.inputInt("번호? ");
board.title = Prompt.inputString("제목? ");
board.content = Prompt.inputString("내용? ");
while (true) {
String name = Prompt.inputString("작성자?(취소: 빈 문자열) ");
if (name.length() == 0) {
System.out.println("프로젝트 등록을 취소합니다.");
return;
} else if (memberHandler.findByName(name) != null) {
board.writer = name;
break;
}
System.out.println("등록된 회원이 아닙니다.");
}
board.registeredDate = new java.sql.Date(System.currentTimeMillis());
boardList.add(board);
}
public void list() {
System.out.println("[게시물 목록]");
Board[] boards = boardList.toArray();
for (Board board : boards) {
System.out.printf("%d, %s, %s, %s, %s\n",
board.no,
board.title,
board.writer,
board.registeredDate,
board.viewCount);
}
}
}
3단계 : 같은 방식으로 다른 핸들러 클래스를 UI 처리와 데이터 처리 클래스로 분리한다.
* 이 밖에 내가 따로 추가해본 기능들
1) 이용자가 직접 각 핸들러들의 인스턴스를 추가하는 기능(App 스태틱 필드로 배열 네개 만들고 각 size 추가)
2) 각 핸들러들의 이름 변수 추가
3) 게시판, 프로젝트, 작업을 추가할 때마다 작성을 허가할 멤버 입력하는 기능
4) 어떤 종류의 멤버, 게시판, 프로젝트, 작업에서 작업을 할 것인지 고르는 기능(리스트 던져주는 메서드도 따로 만들기)
5) 이 기능들을 모두 관리할 클래스 만들기 Manegement.class
package com.eomcs.pms.handler;
import com.eomcs.util.Prompt;
public class TypeHandler {
static final int LENGTH = 100;
static MemberHandler[] memberHandlers = new MemberHandler[LENGTH];
static ProjectHandler[] projectHandlers = new ProjectHandler[LENGTH];
static TaskHandler[] taskHandlers = new TaskHandler[LENGTH];
static BoardHandler[] boardHandlers = new BoardHandler[LENGTH];
static int msize, psize, tsize, bsize;
public static void addMemberType() {
String type = Prompt.inputString("새로 추가할 멤버 종류를 입력하세요. ");
memberHandlers[msize++] = new MemberHandler(type);
System.out.printf("멤버 %s을(를) 추가했습니다.", type);
}
public static void addProjectType() {
String type = Prompt.inputString("새로 추가할 프로젝트 종류를 입력하세요. ");
System.out.print("작성을 허가할 ");
projectHandlers[psize++] = new ProjectHandler(type, mchoose());
System.out.printf("프로젝트 %s을(를) 추가했습니다.", type);
}
public static void addBoardType() {
String type = Prompt.inputString("새로 추가할 게시판 종류를 입력하세요. ");
System.out.print("작성을 허가할 ");
boardHandlers[bsize++] = new BoardHandler(type, mchoose());
System.out.printf("게시판 %s을(를) 추가했습니다.", type);
}
public static void addTaskType() {
String type = Prompt.inputString("새로 추가할 작업 종류를 입력하세요. ");
System.out.print("작성을 허가할 ");
taskHandlers[tsize++] = new TaskHandler(type, mchoose());
System.out.printf("작업 %s을(를) 추가했습니다.", type);
}
public static MemberHandler mchoose() {
while (true) {
System.out.println("멤버 종류를 입력하세요. ");
list(memberHandlers, msize);
String type = Prompt.inputString("");
for (int i = 0; i < msize; i++) {
if (memberHandlers[i].type.equals(type)) {
return memberHandlers[i];
}
}
System.out.println("등록된 멤버가 아닙니다.");
}
}
public static ProjectHandler pchoose() {
list(projectHandlers, psize);
while (true) {
String type = Prompt.inputString("프로젝트 종류를 입력하세요. ");
for (int i = 0; i < psize; i++) {
if (projectHandlers[i].type.equals(type)) {
return projectHandlers[i];
}
}
System.out.println("등록된 프로젝트가 아닙니다.");
}
}
public static BoardHandler bchoose() {
list(boardHandlers, bsize);
while (true) {
String type = Prompt.inputString("게시판 종류를 입력하세요. ");
for (int i = 0; i < bsize; i++) {
if (boardHandlers[i].type.equals(type)) {
return boardHandlers[i];
}
}
System.out.println("등록된 게시판이 아닙니다.");
}
}
public static TaskHandler tchoose() {
list(taskHandlers, tsize);
while (true) {
String type = Prompt.inputString("작업 종류를 입력하세요. ");
for (int i = 0; i < tsize; i++) {
if (taskHandlers[i].type.equals(type)) {
return taskHandlers[i];
}
}
System.out.println("등록된 게시판이 아닙니다.");
}
}
public static void list(Object[] types, int size) {
if (types.getClass().getSimpleName().equals("MemberHandler[]")) {
System.out.println("[멤버 목록]");
for (int i = 0; i < size; i++) {
System.out.println(((MemberHandler[])types)[i].type);
}
} else if (types.getClass().getSimpleName().equals("ProjectHandler[]")) {
System.out.println("[프로젝트 목록]");
for (int i = 0; i < size; i++) {
System.out.println(((ProjectHandler[])types)[i].type);
}
} else if (types.getClass().getSimpleName().equals("BoardHandler[]")) {
System.out.println("[게시판 목록]");
for (int i = 0; i < size; i++) {
System.out.println(((BoardHandler[])types)[i].type);
}
} else {
System.out.println("[작업 목록]");
for (int i = 0; i < size; i++) {
System.out.println(((TaskHandler[])types)[i].type);
}
}
}
}
package com.eomcs.pms;
import com.eomcs.pms.handler.TypeHandler;
import com.eomcs.util.Prompt;
// 1) `Prompt` 클래스를 별도의 패키지로 분류한다
// 2) 핸들러 클래스들을 별도의 패키지로 분류한다
public class App {
public static void main(String[] args) {
loop:
while (true) {
String command = Prompt.inputString("명령> ");
switch (command) {
case "멤버 종류 추가":
TypeHandler.addMemberType();
break;
case "프로젝트 종류 추가":
TypeHandler.addProjectType();
break;
case "게시판 추가":
TypeHandler.addBoardType();
break;
case "작업 종류 추가":
TypeHandler.addTaskType();
break;
case "/member/add":
TypeHandler.mchoose().add();
break;
case "/member/list":
TypeHandler.mchoose().list();
break;
case "/project/add":
TypeHandler.pchoose().add();
break;
case "/project/list":
TypeHandler.pchoose().list();
break;
case "/task/add":
TypeHandler.tchoose().add();
break;
case "/task/list":
TypeHandler.tchoose().list();
break;
case "/board/add":
TypeHandler.bchoose().add();
break;
case "/board/list":
TypeHandler.bchoose().list();
break;
case "quit":
case "exit":
System.out.println("안녕!");
break loop;
default:
System.out.println("실행할 수 없는 명령입니다.");
}
System.out.println(); // 이전 명령의 실행을 구분하기 위해 빈 줄 출력
}
Prompt.close();
}
}
느낀점 : 코드로 똥을 싼다는 것이 이런 거구나!
- Handler끼리 공통점이 너무 많아서 메서드마다 중복된 코드가 많다. 상속을 해주는 슈퍼클래스가 있으면 관리가 쉬워질 것 같다. 그렇게만 한다면 타입클래스의 메서드가 4분의 1로 줄어들 것이다.
- 사실 써놓고 어디서부터 고쳐야될지 잘 모르겠다. 이래서 리팩토링을 배워야하나보다;;
'국비 교육' 카테고리의 다른 글
2020.8.13일자 수업 : Object 클래스 (0) | 2020.08.20 |
---|---|
2020.8.17일자 수업 : ArrayList, LinkedList (0) | 2020.08.17 |
2020.8.14일자 수업 : String, Wrapper, ArrayList 구현 실습 (0) | 2020.08.15 |