본문 바로가기
개발자 꿈나무의 하루/01_Boot Camp

(네이버클라우드 부트캠프) 19일차 - 실습프로젝트(Static필드, High Cohesion)

by kk_naks 2024. 6. 20.

1. 데이터 식별 번호 부여하기

  1.1 데이터식별

- 회원, 프로젝트, 게시글에 대한 고유 번호 생성
- 배열 중간에 인스턴스를 삭제하여도 고유번호 유지
- static feild활용
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 메서드 생성
- 인스턴스 필드에 스태틱필드값 (시퀀스넘버)를 전달할 메서드 생성
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;
  
	//기타 인스턴스 필드 생량//

  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;
  
	//기타 인스턴스 필드 생량//

  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;
  
	//기타 인스턴스 필드 생량//

  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 문 시작인덱스 변경
  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. High Cohesion 패턴 적용

  2.1 GRASP 방법론

     - GRASP(General Responsibility Assignment SofeWare Patters)

     - 객체지향 설계에서 객체에게 책임을 할당하는 방법론

1. Information Expert (정보 전문가): 특정 작업을 수행하는 데 필요한 정보를 가지고 있는 객체에게 그 작업을 할당

2. Creator (생성자): 객체를 생성할 책임을 특정 객체에게 부여하고 생성자는 생성될 객체와 밀접한 관계가 있는 객체로 설정

3. Controller (컨트롤러): 시스템 이벤트를 처리할 책임을 가지며, 시스템의 주요 제어 흐름을 담당

4. Low Coupling (낮은 결합도): 객체 간의 의존성을 최소화

5. High Cohesion (높은 응집도): 객체가 담당하는 책임을 관련된 것들로 모아 하나의 주제로 결집

6. Polymorphism (다형성): 다형성을 사용하여 동일한 인터페이스를 통해 다양한 객체를 처리

7. Pure Fabrication (순수 가공): 설계 문제를 해결하기 위해 실제 도메인 모델에는 없는 추가적인 클래스를 만들어 사용

8. Indirection (간접화): 두 개체 간의 직접적인 연결을 피하고, 간접적인 연결을 통해 결합도를 낮춤

9. Protected Variations (변화 보호): 변화 가능성이 있는 부분을 캡슐화하여 외부의 영향을 최소화

 

  2.2 High Cohesion(높은 응집도)

- 기존 command class의 구조 : 서브메뉴들의 흐름제어 + 데이터보관처리
- 하나의 객체가 2가지의 역할을 담당하고 있으므로 클래스 분리가 필요하다. 
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;
    
  //데이터처리 메서드 
  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;

  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 {

  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("없는 회원입니다.");
    }
  }

}

 

     - 다른 클래스 파일도 동일하게 적용