본문 바로가기

JAVA/DAY 14 _ 23.09.05

Student Manager Program _ V1

 

# Main

package com.ja.stm.main;

import com.ja.stm.component.Wrapper;

public class Main {				// 1)

	public static void main(String[] args) {

//		Wrapper w = new Wrapper();	
//		w.run();				  ---> 변수가 필요할 때.
		
		new Wrapper().run();	// --> 실행 시켜야 할 때.
	
	}

}

 

# IoUtil

package com.ja.stm.util;

import java.util.Scanner;

public class IoUtil {		// (--> input. output. util)
	
	private static Scanner scanner = new Scanner(System.in);
	
	// 4) 자주사용할 코드. 매서드마다 매번 부르기 부담있었지?? 위로 올려서 인스턴스로 생성해주자! 
	// 근데 밑에가 static이면 인스턴스는 못쓰니까 얘도 static붙여주기 
	
	
	public static void println(String text) {		
		// 2) syso를 바깥에서 쓰지말고, 이 매서드를 사용해야하겠지
		// 2-1) static 변수는 같은 패키지 내에서 어디에서나 접근할 수 있음
		// 2-3) 패키지 외부에서 static 변수에 접근하려면 해당 변수를 public으로 선언해야겠지
	
		System.out.println(text);
	}
	
	
	public static String input(String text) {		
	
		System.out.print(text);
		String input = scanner.nextLine();	
		return input;								
		// 3)
		// return scanner.nextLine(); 으로 써도됨. 이렇게 쓸 수 있도록 하자!
		// scanner.nextLine(); --> 이거 자체가 String이니까.
	}
	
	
	public static void pause() {			// 3-1)
		
		System.out.print("계속하시려면 enter를 입력하세요 > ");
		scanner.nextLine();
	}

 

# Wrapper

public class Wrapper {
	
//  public void test(){
	
//	IoUtil.println("안녕");					// syso용 메서드 호출
//	String input = IoUtil.input("입력 > ");	// input용 메서드 호출 -> input값을 return해주는 애. 
//	IoUtil.pause();						// enter치면 종료? 돌아가는? 메서드 호출
//	
//	System.out.println(input);		---> run의 String input변수는 그 return값을 출력한다
//	}
	
	
	private CommandHandler commandHandler = new CommandHandler();		
// 11) case마다 --> new CommandHandler().addStudentInfo();
// 이렇게 써도 실행은! 되겠지만..... case를 나가는 순간 메모리가 소멸된다. 
// ★★★ 그니까 맨위로 올려서 인스턴스 변수로 지정해줘야함 ★★★. class명과 똑같이 쓰되, 맨앞은 소문자!
// private CommandHandler commandHandler; --> 이렇게만 두면 null값으로, exception error난다.
// Wrapper를 호출하는 순간 commandHandler 인스턴스 변수가 생성된다. (강한 관계)
// commandHandler는 Wrapper의 주소값을 받는다.
public void run() {				
	
    printWelcomeMessage();					// 5-1)

    while(true) {						// 7) 뼈대를 만들자

        printCommandMenu();					// 8-1)
        String input = IoUtil.input("사용하려는 메뉴의 번호를 입력하세요 > ");

        if(input.equals("q")) break;	// 8-2) 중괄호 하나면 굳이 안써도 되긴함.


        switch(input) {			// 9) if문인데, 웬만하면 switch case는 일반적으로 잘사용하지 않는다!

            case "1":
                commandHandler.addStudentInfo();
                break;

            case "2":
                commandHandler.printStudentList();
                break;

            case "3":
                commandHandler.searchStudentInfo();
                break;

            case "4":
                commandHandler.deleteStudentInfo();
                break;

            case "5":
                commandHandler.printStatistics();
                break;

            default:
                IoUtil.println("잘못된 입력입니다. 다시 입력하세요");	// 9-1)
        }
        IoUtil.pause();			// 9-2) 이건 매번 나와야하는 입력이니까 case 바깥에 있어야겠지?

    }
    printByeMessage();			// 6-1)

}
private void printCommandMenu() {				// 8)
		
		IoUtil.println("==== MENU ====");
		IoUtil.println("1. 정보 입력");
		IoUtil.println("2. 리스트 출력");
		IoUtil.println("3. 정보 검색");
		IoUtil.println("4. 정보 삭제");
		IoUtil.println("5. 점수 통계");
		IoUtil.println("q. 프로그램 종료");
	}
	
	
	
	
	private void printWelcomeMessage() {			// 5)
		
		IoUtil.println("===== 학생정보 관리 프로그램 V2 =====");	
	}
	
	
	private void printByeMessage() {	
    // 6) scnner.close();는 굳이굳이 쓰려면 쓸수는있짐나 여기서는 안해도 될듯여

		IoUtil.println(" ");
		IoUtil.println("프로그램을 이용해주셔서 감사합니다");
		IoUtil.println("=== 프로그램이 종료됩니다 ===");
//		Scanner.close(); --> "cannot make a static reference to the non-static method"
	}

 

# Command Handler

public class CommandHandler {			// 10) 각 번호에 해당하는 메서드를 만드는 곳, 뼈대 먼저 만들기
	
	
	private DataModel dataModel = new DataModel();	
    // 15) datamodel 클래스가 commandhandler에 완전히 종속되도록!
	
	
	
	public void addStudentInfo() {		// 12) 메서드 내용 잡기
		
		IoUtil.println("=== 정보득록(현재 입력된 학생 수 : " + dataModel.getCount() + ") ===");		
		String name = IoUtil.input("이름을 입력하세요 > ");
		int age = Integer.parseInt(IoUtil.input("나이를 입력하세요 > "));
		int score = Integer.parseInt(IoUtil.input("점수를 입력하세요 > "));
		
	
		StudentDto student = new StudentDto(name, age, score);			
		// 14) 생성자 선언을 해주고.... 이제 배열을 만들어야하는데 어디다가 할까? 
      		// --> array관리하는 클래스를 또 만들어야겠찌?
		dataModel.add(student);			// 15-1)
		
		IoUtil.println("학생 정보가 등록되었습니다");
		

	}
public void printStudentList() {

    IoUtil.println("=== 학생정보 리스트 출력 ===");		// 17)

    // int count = dataModel.getCount();			// 17-9) 이제 count변수를 굳이 안써도된다!
    StudentDto[] list = dataModel.getList();

    // list[0] = null;								
    --> 17-2) 누군가 이런식으로 실수를 할 경우? 원본 studentList가 손상된다.
	// 17-5) list[0] = null; 을 해도 원본은 유지 되겠지.

    // list[1].setAge(70);								
    --> 17-6) 또!!! 원복이 손상되는 코드를 써놨다?
    // (cloneList를 한번만 따라가면 원본에 도달하니까 원본손상이 쉽다)


    // for(int x = 0 ; x < count ; x++) {		--> 18)이제 이거말고 foreach쓰자

    for(StudentDto studentDto : list) {

    // 	StudentDto studentDto = list[x];			18-1) 이것도 필요 x

        String text = "이름 : " + studentDto.getName();	
        // 17-1) +++써서 길~게 썼던거. += 가능하다!!! ★

        text += ", / 나이 : " + studentDto.getAge();

        text += ", / 점수 : " + studentDto.getScore();

        IoUtil.println(text);

    }

    IoUtil.println("정보 입력된 학생 수 : " + list.length);	// 18-2) count대신 length쓰면 되겠지?

}
public void searchStudentInfo() {						// 19)

    IoUtil.println("=== 학생 정보 검색 ===");
    String search = IoUtil.input("검색할 학생의 이름을 입력하세요 > ");


    StudentDto[] list = dataModel.getList();		// 19-1) 깊은 복사했던거 끌고오기

    int searchCount = 0;

    for(StudentDto studentDto : list) {

        if(!studentDto.getName().contains(search)) continue;	
        // 19-2) 동일한 내용이 없으면 계속 반복 돌기, 있으면 출력.

        String text = "이름 : " + studentDto.getName();	

        text += ", / 나이 : " + studentDto.getAge();

        text += ", / 점수 : " + studentDto.getScore();

        IoUtil.println(text);

        searchCount++;
    }

    IoUtil.println("검색 된 학생 수 : " + searchCount);

}
public void deleteStudentInfo() {

    IoUtil.println("=== 학생 정보 삭제 ===");						
    // 20) 복사본을 삭제하면 의미가 없겠찌? 원본을 복사하도록하자 
    --> 그걸 수행할 메소드 만들러가기~
    String delete = IoUtil.input("삭제할 학생의 이름을 입력하세요 > ");

    StudentDto[] list = dataModel.getList();					
    // ++ 삭제할 학생이름을 검색했어? 뭐가 삭제되는지 보여주고싶어.

    for(StudentDto studentDto : list) {

        if(!studentDto.getName().equals(delete)) continue;		
        // ++ 검색 했던 그 코드 그대로 복붙해옴!!!
        (count만 delete로 바꿔주면되겠지)

        String text = "이름 : " + studentDto.getName();	

        text += ", / 나이 : " + studentDto.getAge();

        text += ", / 점수 : " + studentDto.getScore();

        IoUtil.println(text);
    }



    int count = dataModel.remove(delete);						
    // 20-4) return한 값을 변수로 받아


    IoUtil.println(count + "명의 학생 정보가 삭제되었습니다");

}
public void printStatistics() {

    IoUtil.println("=== 통   계 ===");		// 21) 합, 평균
    StudentDto[] list = dataModel.getList();

    int sum = 0;

    for(StudentDto studentDto : list) {

        sum += studentDto.getScore();
    }

    double average = sum/(double)list.length;

    IoUtil.println("총 인원 : " + list.length);
    IoUtil.println("평균 점수 : " + average);

}

 

# Student Dto

package com.ja.stm.dto;

// DTO, VO :					// 13)

// 개념적 데이터 구조체로서의 클래스.
// 일반적으로 필드(인스턴스 변수 생성하는 곳)가 중요하다!


public class StudentDto {		
// Data transfer object : 이름, 나이, 성적을 한번에 묶을 class

	private String name;
	private int age;
	private int score;
	
	public StudentDto() {		
    // 13-1) 기본 생성자는 안쓰더라도 만들어주자	
	}
	
	
	public StudentDto(String name, int age, int score) {		
    // 13-2) 모든 인스턴스를 포함한 생성자도 기본!!!
		this.name = name;
		this.age = age;
		this.score = score;
	}
	
	public void setName(String name) {		// 13-3) setter, getter
		this.name = name;	
	}
	
	public String getName() {
		return name;
	}
	
	public void setAge(int age) {
		this.age = age;
	}

	public int getAge() {
		return age;
	}
	
	public void setScore(int score) {
		this.score = score;
	}
	
	public int getScore() {
		return score;
	}
}

 

# Data Model

public class DataModel {			// 14-1) array를 관리하는 클래스.

private StudentDto[] studentList = new StudentDto[10];
// --> 밑에다 넣으면 메모리의 life cycle이 매우 짧아지므로, 멤버로 승격시켜줘. (어차피 계~속 쓰는거라 계속 new하는것도 일이야.)
// --> StudentDto의 인스턴스가 10개 생기는게 아님!!! (이건 new.. 를 10개 만들어야함)
// --> StudentDto 주소값을 다룰 수 있는 참조주소가 10개 생긴거임 (= null 10개)

private int count;


public void add(StudentDto studentDto) {	// 14-2) studentDto의 참조주소를 받을거야

//		StudentDto student1 = new StudentDto("학생1", 20, 90);
//		StudentDto student2 = new StudentDto("학생2", 21, 85);
//		StudentDto student3 = new StudentDto("학생3", 22, 88);
//
//		arrangeInfo(student1);
//		arrangeInfo(student2);
//		arrangeInfo(student3);				


// 		 for(int x = 0 ; x < count ; x++){	
//			studentList[x] = studentDto;
// 		 }
// --> 이건안돼. studentDto 객체를 모든 인덱스에 동일한 내용으로 덮어쓰게 되잖아.


// for 루프는 여러 개의 학생 정보를 studentList 배열에 추가하는 방법을 나타냄
// 즉, '여러 학생 정보를 한 번에 처리하고 배열에 저장'하는 루프



    studentList[count] = studentDto;	
    count++;
    // 14-3) 한줄의 정보를 입력받으면 그 정보가 List[0]에 들어갈거야
    // count를 계속 쌓을 수 있겠지
    // studentDto 객체를 studentList 배열의 다음 위치에 저장하고 count를 증가시킴
    // 이 메서드는 학생 '정보를 하나씩 studentList 배열에 추가'하는 역할을 함

    if(studentList.length >= count) {		
    // == 이 아주 정확한 표현이긴하지만, 버그가 무섭잖아?

        StudentDto[] newList = new StudentDto[studentList.length *2];

        for(int x = 0 ; x < count ; x++) {

            newList[x] = studentList[x];		// 14-4) 배열길이를 늘리고, 내용을 복사하는 과정	
        }
        studentList = newList;		// 14-5) 복사는했으니, 옛날 USB는 버려야겠지


    }

}
public StudentDto[] getList() {							
// 16) StudentDto의 배열(= 참조주소를)을 return하는것 ★★★★
// --> StudnetDto가 아니라, 배열의 참조주소를 return하는것을 잊지마요오오
// --> list를 return을 하고싶으니까, StudentDto[]를 타입으로 써야지?

    // return studentList;		// 17-3) 원본은 그대로 남도록, 복사본을 만들어서 list를 return할거야

    StudentDto[] cloneList = new StudentDto[count];

//		for(int x = 0 ; x < count ; x++) {		
// 17-4) (light하게)복사하는 과정(있는것만 복사하면되니까 그냥 count까지만 복사하자)
//			
//			cloneList[x] = studentList[x];					
//		}
// ---> 즉, cloneList를 한번만 따라가면 원본에 도달하니까 원본손상이 쉽다는 얘기지?
//		
//		return cloneList;


    // 17-7) deep하게 복사하자 (깊은 복사 과정....)
    // --> list만 복사되는게아니라, StudentDto에 입력받은 값 자체가 복사되야한다는거!

    for(int x = 0 ; x < count ; x++) {

        StudentDto newStudentDto = new StudentDto

                (
                studentList[x].getName(),
                studentList[x].getAge(),
                studentList[x].getScore()
                );

        cloneList[x] = newStudentDto;
    }

    return cloneList;		// 17-8) 전체를 복사한 list를 return.....
}
public int remove(String name) {	
// 20-1) return으로 몇명지웠는지 돌려주기위해 int타입으로 메서드를 만들자

    int removeCount = 0;

    for(int x = 0 ; x < count ; x++) {

        StudentDto studentDto = studentList[x];

        if(studentDto.getName().equals(name)) {			
        // 20-2) x위치부터 정보를 밀어넣자!(= 삭제)

            for(int y = x ; y < count -1 ; y++) {

                studentList[y] = studentList[y+1];		
                // 20-3) [0]의 이름을 삭제? [0] = [1] 이걸 반복해서 [0]을 밀어내자.★★★
            }
            count--;
            x--;
            removeCount++;

        }	
    }
    return removeCount;
}

'JAVA > DAY 14 _ 23.09.05' 카테고리의 다른 글

Debug mode  (0) 2023.09.11