Day19 실습프로젝트(Static필드, High Cohesion)
1. 데이터 식별 번호 부여하기
1.1 데이터식별
- 회원, 프로젝트, 게시글에 대한 고유 번호 생성
- 배열 중간에 인스턴스를 삭제하여도 고유번호 유지
- static feild활용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Counter {
static int count = 0;
Counter() {
this.count++;
System.out.println(this.count);
}
}
public class Sample {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
}
}
- 스태틱 필드는 클래스의 고유 필드로 적용
- 따라서 인스턴스 메서드로 선언하는 것이 아닌 클래스 필드를 생성
- 전역변수와 같은 역할을 하게 된다.
1.2 데이터 클래스에 seqNo 적용
- 데이터 클래스에 시퀀스넘버를 생성
- 시퀀스넘버를 접근하는 getter 메서드 생성
- 인스턴스 필드에 스태틱필드값 (시퀀스넘버)를 전달할 메서드 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Object{
//static 필드생성
private static int seqNo;
//인스턴스 필드생성
private int no;
//seqNo getter메서드
public int getSeqNo(){
return ++seqNo;
}
//no getter 및 setter 메서드
public setNo(int no){
this.no = no;
}
public getNo(){
return no;
}
}
User class
package bitcamp.myapp2.vo;
public class User { private static int seqNo; private int no;
//기타 인스턴스 필드 생량//1 2 3 4 5 6 7 8 9 10 11 12 13
public static int getSeqNo() { return ++seqNo; } public int getNo() { return no; } public void setNo(int no) { this.no = no; } //getter setter 생략// } }
- Board class. Project class 도 동일하다
Board class
package bitcamp.myapp2.vo;
public class User { private static int seqNo; private int no;
//기타 인스턴스 필드 생량//1 2 3 4 5 6 7 8 9 10 11 12 13
public static int getSeqNo() { return ++seqNo; } public int getNo() { return no; } public void setNo(int no) { this.no = no; } //getter setter 생략// } }
Project class
package bitcamp.myapp2.vo;
public class User { private static int seqNo; private int no;
//기타 인스턴스 필드 생량//1 2 3 4 5 6 7 8 9 10 11 12 13
public static int getSeqNo() { return ++seqNo; } public int getNo() { return no; } public void setNo(int no) { this.no = no; } //getter setter 생략// } }
1.2 Command 클래스 파일 수정
- addUser()메서드에 setNo()메서드 추가
- listUser()메서드에 for문 출력문에 getNo()메서드 추가
- findByNo()메서드에 userNo탐색 방법 변경
- 유효성검증 방식 변경(viewUser, updateUser, deleteUser)
- 인덱스 리턴 메서드 추가
- deleteUser() for 문 시작인덱스 변경
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
private static void addUser() {
User user = new User();
user.setNo(User.getSeqNo());
//이하 동일//
}
private static void listUser() {
System.out.println("번호 이름 이메일");
for (int i = 0; i < userLength; i++) {
User user = users[i];
// (i+1) -> user.getNo()로 변경
System.out.printf("%d %s %s\n", user.getNo(), user.getName(), user.getEmail());
}
}
public static User findByNo(int userNo) {
// if (범위) 탐색 -> for 문 탐색
for (int i = 0; i < userLength; i++) {
User user = users[i];
if (user.getNo() == userNo) {
return user;
}
}
return null;
}
//인덱스 리턴 메서드 추가
private static void viewUser() {
int userNo = Prompt.inputInt("회원번호?");
//유효성 검증 -> for문 탐색으로 user인스턴스 리턴
User user = findByNo(userNo);
if (user == null) {
System.out.println("없는 회원입니다.");
return;
}
//이하 동일//
}
public static int indexOf(User user){
for (int i = 0; i < userLength; i++){
if(users[i] == user){
return i;
}
}
return -1;
}
private static void deleteUser() {
int userNo = Prompt.inputInt("회원번호?");
User user = findByNo(userNo);
if (user == null) {
System.out.println("없는 회원입니다.");
return;
}
//해당 인덱스 리턴 메서드
int index = indexOf(user);
//userNo가 아닌 index+1로 시작
for (int i = index + 1; i < userLength; i++) {
users[i - 1] = users[i];
}
users[--userLength] = null;
System.out.println("삭제했습니다.");
}
- 다른 command 클래스도 동일하게 적용
2.1 GRASP 방법론
GRASP(General Responsibility Assignment SofeWare Patters)
객체지향 설계에서 객체에게 책임을 할당하는 방법론
Information Expert (정보 전문가): 특정 작업을 수행하는 데 필요한 정보를 가지고 있는 객체에게 그 작업을 할당
Creator (생성자): 객체를 생성할 책임을 특정 객체에게 부여하고 생성자는 생성될 객체와 밀접한 관계가 있는 객체로 설정
Controller (컨트롤러): 시스템 이벤트를 처리할 책임을 가지며, 시스템의 주요 제어 흐름을 담당
Low Coupling (낮은 결합도): 객체 간의 의존성을 최소화
High Cohesion (높은 응집도): 객체가 담당하는 책임을 관련된 것들로 모아 하나의 주제로 결집
Polymorphism (다형성): 다형성을 사용하여 동일한 인터페이스를 통해 다양한 객체를 처리
Pure Fabrication (순수 가공): 설계 문제를 해결하기 위해 실제 도메인 모델에는 없는 추가적인 클래스를 만들어 사용
Indirection (간접화): 두 개체 간의 직접적인 연결을 피하고, 간접적인 연결을 통해 결합도를 낮춤
Protected Variations (변화 보호): 변화 가능성이 있는 부분을 캡슐화하여 외부의 영향을 최소화
2.2 High Cohesion(높은 응집도)
- 기존 command class의 구조 : 서브메뉴들의 흐름제어 + 데이터보관처리
- 하나의 객체가 2가지의 역할을 담당하고 있으므로 클래스 분리가 필요하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ProjectCommand {
//데이터보관 필드
private static final int MAX_SIZE = 10;
private static final Project[] projects = new Project[MAX_SIZE];
private static int projectLength = 0;
//UI처리 메서드
public static void excuteProjectCommand(String command){}
private static void addProject() {}
private static void listProject() {}
private static void viewProject() {}
private static void updateProject() {}
private static void deleteProject() {}
private static void addMembers(Project project) {}
private static void deleteMembers(Project project) {}
//데이터처리 메서드
private static Project findByNo(int projectNo) {}
private static int indexOf(Project project) {}
}
- 위 클래스를 2개의 클래스로 분리
- 데이터보관 및 처리 메서드 // UI처리 메소드
- 이를 통해 로직의 복잡성을 낮추고 유지 보수를 용이하게 한다.
public class ProjectList { //데이터보관 필드 private static final int MAX_SIZE = 10; private static final Project[] projects = new Project[MAX_SIZE]; private static int projectLength = 0;
1 2 3
//데이터처리 메서드 private static Project findByNo(int projectNo) {} private static int indexOf(Project project) {} }
public class ProjectCommand { //UI처리 메서드 public static void excuteProjectCommand(String command){} private static void addProject() {} private static void listProject() {} private static void viewProject() {} private static void updateProject() {} private static void deleteProject() {} private static void addMembers(Project project) {} private static void deleteMembers(Project project) {}
}
2.3 클래스 분리하기
- UserList 클래스 생성
- 유저정보를 저장하는 클래스 필드 생성
- 유저정보들을 저장하는 유저배열 생성 메서드
- 유효한 유저정보를 삭제하는 메서드
- 유효한 유저수만큼 배열 조정하는 메서드
- 유효한 유저 정보를 반환하는 메서드
- 유저 정보 인덱스를 반환하는 메서드
package bitcamp.myapp2.command;
import bitcamp.myapp2.vo.User;
public class UserList { private static final int MAX_SIZE = 10; private static final User[] users = new User[MAX_SIZE]; private static int userLength = 0;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
public static void add(User user) { users[userLength++] = user; } public static User delete(int userNo) { User deleteUser = findByNo(userNo); if (deleteUser == null) { return null; } int index = indexOf(deleteUser); for (int i = index + 1; i < userLength; i++) { users[i - 1] = users[i]; } users[--userLength] = null; return deleteUser; } public static User[] toArray() { User[] arr = new User[userLength]; for (int i = 0; i < arr.length; i++) { arr[i] = users[i]; } return arr; } public static User findByNo(int userNo) { for (int i = 0; i < userLength; i++) { User user = users[i]; if (user.getNo() == userNo) { return user; } } return null; } public static int indexOf(User user) { for (int i = 0; i < userLength; i++) { if (users[i] == user) { return i; } } return -1; } }
- UserCommand 클래스 수정
- 기존 Command 메서드 중 명령 관련 메소드로 구성
- addUser()메서드 추가
- viewUser() for문 호출 방식 변경
- deleteUser() 메서드 변경
package bitcamp.myapp.command;
import bitcamp.myapp.util.Prompt; import bitcamp.myapp.vo.User;
public class UserCommand {
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
public static void executeUserCommand(String command) { System.out.printf("[%s]\n", command); switch (command) { case "등록": addUser(); break; case "조회": viewUser(); break; case "목록": listUser(); break; case "변경": updateUser(); break; case "삭제": deleteUser(); break; } } private static void addUser() { User user = new User(); user.setName(Prompt.input("이름?")); user.setEmail(Prompt.input("이메일?")); user.setPassword(Prompt.input("암호?")); user.setTel(Prompt.input("연락처?")); user.setNo(User.getNextSeqNo()); UserList.add(user); } private static void listUser() { System.out.println("번호 이름 이메일"); for (User user : UserList.toArray()) { System.out.printf("%d %s %s\n", user.getNo(), user.getName(), user.getEmail()); } } private static void viewUser() { int userNo = Prompt.inputInt("회원번호?"); User user = UserList.findByNo(userNo); if (user == null) { System.out.println("없는 회원입니다."); return; } System.out.printf("이름: %s\n", user.getName()); System.out.printf("이메일: %s\n", user.getEmail()); System.out.printf("연락처: %s\n", user.getTel()); } private static void updateUser() { int userNo = Prompt.inputInt("회원번호?"); User user = UserList.findByNo(userNo); if (user == null) { System.out.println("없는 회원입니다."); return; } user.setName(Prompt.input("이름(%s)?", user.getName())); user.setEmail(Prompt.input("이메일(%s)?", user.getEmail())); user.setPassword(Prompt.input("암호?")); user.setTel(Prompt.input("연락처(%s)?", user.getTel())); System.out.println("변경 했습니다."); } private static void deleteUser() { int userNo = Prompt.inputInt("회원번호?"); User deletedUser = UserList.delete(userNo); if (deletedUser != null) { System.out.printf("'%s' 회원을 삭제 했습니다.\n", deletedUser.getName()); } else { System.out.println("없는 회원입니다."); } }
}
- 다른 클래스 파일도 동일하게 적용