DAO, DTO를 활용해서 학생 성적 정보 입력하기(get,set)
package java0517;
import java.util.Scanner;
public class StudentCap {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
StudentDao dao = new StudentDao();
while(true) {
System.out.println("어떤 업무를 원하십니까?");
System.out.println("1.학생 추가");
System.out.println("2.학생 삭제");
System.out.println("3.학생 검색");
System.out.println("4.학생 수정");
System.out.println("5.모두 출력");
System.out.print("업무 번호 입력 >>");
int key = sc.nextInt();
switch(key) {
case 1 :
System.out.println("학생 추가");
dao.insert();
break;
case 2 :
System.out.println("학생 정보 삭제");
dao.delete();
break;
case 3 :
System.out.println("학생 정보 검색");
dao.select();
break;
case 4 :
System.out.println("학생 정보 수정");
dao.update();
break;
case 5 :
System.out.println("학생 전체 출력");
dao.allPrint();
break;
default : System.out.println("다시 입력해주세요");
}
}
}
}
캡슐화를 배우면서 응용하는 예제로 클래스의 이름은 Student와 Encapsulation의 cap을 따서 만들어봤습니다. 이 코드는 메인함수가 들어있는 클래스로, 실질적으로 만들어놓은 메소드가 실행이 되는 부분입니다.
처음 public static void main(String[] args)를 적고 나서 제일 먼저 해야 할 일은 스캐너 기능을 넣어주는 것입니다. 사용자로부터 콘솔에 입력을 받아 정보를 입력할 것이기 때문입니다. 이후에 StudentDao 클래스의 dao 인스턴스를 new 예약어를 사용하여 생성해줍니다.
while(true) 조건문을 감싸준 이유는, 한 사람만 입력하고 끝날 것이 아니라, 사용자가 필요한 만큼 계속해서 실행이 되어야 하기 때문입니다. 따라서 특정 작업을 수행하고 나서도 다시 메뉴로 돌아오게끔 만들었습니다. 선택할 수 있는 메뉴의 선택지를 쭉 보여준 다음에 필요한 업무 번호 입력을 요구합니다. 여기서 스캐너로 받은 숫자를 저는 int key로 만들었습니다. 그러면 학생 수정을 하고 싶다면 4를 누를것이고, 학생 정보 입력을 하고 싶다면 1을 누르겠죠? 그 숫자에 맞게 다른 작업이 진행되어야 하기 때문에 이 부분은 깔끔하게 정리하기 위해서 swtich 케이스를 사용하였습니다. 각각의 케이스에는 해당되는 기능을 넣어줬습니다. 그리고 마지막에 디폴트값으로는 혹시나 1~5사이의 숫자를 누르지 않고 다른 숫자를 누를 사람을 대비해서 다시 입력하고 친절하게 안내까지 해줍니다. 1번은 dao.insert(); 인데요, 여기서 앞에 나온 StudentDao 클래스를 살펴보겠습니다. 코드의 길이가 길어서 조금씩 잘라서 설명하고 마지막에 전체 코드를 올리도록 하겠습니다.
package java0517;
import java.util.Scanner;
//Data Access Object 실제로 데이터에 접근하는 객체
public class StudentDao {
Scanner sc;
private StudentDto stArray[] =null;
private int count;
public StudentDao() {
sc = new Scanner(System.in);
stArray = new StudentDto[20];
count = 0;
}
StudentDao 클래스의 시작부분입니다. 여기서도 main함수에서처럼 스캐너를 쓰게 되는데요, main함수에서의 스캐너 역할은 어떤 업무를 하고 싶은건지? 확인하는 용도였다면 여기에서는 실질적으로 학생의 이름, 성적과 같은 것을 입력하는 용도입니다. 학생의 이름과 성적이 노출되지 않게 하기 위해서 StudentDto 클래스에 stArray 배열을 만들어주고 null로 선언하였습니다. 또한 입력받는 학생들의 순번을 지정할 수 있게끔 int형 count도 하나 만들어주고 0으로 초기화하였습니다. 처음에 public class StudentDao 안에 배열을 먼저 선언만 하고 길이는 studentDao() 메소드에서 한 이유는 어차피 초기화는 한 번만 해줄것이기 때문에 지역변수로 만들었습니다.
<Data Access Object>
1. 학생 정보 입력
//TODO 추가 C insert
public void insert() {
//입력
System.out.println("학생의 이름을 입력해주세요");
System.out.print("이름 : ");
String name = sc.next();
System.out.print("국어 : ");
int kor = sc.nextInt();
System.out.print("영어 : ");
int eng = sc.nextInt();
System.out.print("수학 : ");
int math = sc.nextInt();
//배열에 추가
stArray[count] = new StudentDto(name, kor, eng, math);
count++;
System.out.println(stArray[0]);
}
이제 학생들을 한 명씩 입력해보겠습니다. 이 부분은 크게 어려움이 없이 이해가 될것입니다. 마지막 부분에서 배열에 넣는 것이 조금 헷갈릴 수 있는데, 만약 1명의 학생을 우선적으로 입력한다고 가정해본다면, {홍길동, 40, 70, 50} 이렇게 하나의 인스턴스가 stArray[0]에 들어가게 될 것입니다. 본래 배열은 같은 자료형만 함께 담을 수 있는데 인스턴스를 사용하게 되면 String인 홍길동과 int인 40이 함께 담길 수 있게 됩니다. 1명의 학생을 완전하게 입력하고 나면 count++;을 통해 count는 1이 되고 다시 또 메뉴로 돌아가게 됩니다. 제대로 입력이 들어갔는지를 확인하기 위해 저는 제일 마지막 줄에 System.out.println(stArray[0]);을 적어서 확인했습니다. 코드를 다 짜놓고 나서 다시 헤집으려면 힘들기 때문에 중간중간에 체크하는 습관을 들이려고 노력중입니다.
2. 학생 정보 삭제
예를 들어, 그럴 확률은 낮겠지만 다른 반 학생의 정보를 실수로 우리 반에 입력해버렸습니다. 그렇다면 그 학생의 정보를 삭제해야겠죠. 여기서는 어떻게 이미 입력한 정보를 삭제하는지 부분입니다. 일단, 누구를 삭제할지 이름을 받아야합니다. 스캐너를 활용합니다. 그런 다음에 int index를 만들어주는데요, search함수에서 해당 이름을 매개변수로 받아 보내준 값을 index로 하기로 합니다. 그럼 여기서 갑툭튀한 search 함수부터 건드리고 가야할 것 같습니다.
* 학생 찾아내기
public int search(String name) {
int index = -1;
for(int i = 0; i<stArray.length;i++) {
if(stArray[i]!=null&&!stArray[i].getName().equals("")) {
if(name.equals(stArray[i].getName())) {
index = i;
break;
}
}
}
return index;
}
학생을 삭제하기 위해서도, 수정하기 위해서도 그 해당 학생을 일단 찾아야 할 것입니다. 이 함수는 그 용도입니다.
먼저 어떤 함수든지 필요하다면 사용할 수 있게 해야하기 때문에 public, 반환하는 자료형이 int형이기 때문에 public int가 되며 함수명은 search로 합니다. 매개변수는 String name입니다. int index = -1;이 눈에 띌 것입니다. 보통 초기화는 0으로 많이 해줬는데 왜 이번에는 -1으로 한 것일까요? 그것은 바로 stArray[0]부터 시작하기 때문입니다. 앞서서 정보를 입력할 때 count를 0으로 해서 시작한 것을 떠올리면 연관되어 이해가 바로 될 것입니다. if조건문을 만족할 때 index가 변경이 되어야 하는데, 만약 index를 0으로 초기화해놓고 시작한다면 제대로 추려내지 못할 수 있겠습니다. 그래서 0~시작하는 숫자와 전혀 상관없고, 절대 나올 일도 없는 -1로 초기화한 것입니다.
for문을 돌려줍니다. stArray 배열의 모든 학생을 샅샅이 살펴봅니다. if조건은 null값을 가지고 있지 않고, 이름이 공백이 아닌 학생을 기준으로, 만약 우리가 삭제하기 위해서 입력했던 그 이름과, stArray[i].getName()이 같다면, 잡았다 요놈하고 바로 index를 그 학생의 i값으로 바꿔주고 종료한다는 것입니다. return값은 바로 그 index입니다.
여기서 가져온 index를 가지고 이제는 본격적으로 학생 정보를 삭제하는 부분입니다.
//TODO 삭제 D delete
public void delete() {
System.out.print("삭제할 학생 이름 : " );
String name = sc.next();
int index = search(name);
if(index == -1) {
System.out.println("학생 데이터를 찾지 못했습니다.");
return;
}else {
System.out.println(stArray[index].toString());
}
stArray[index].setName("");
stArray[index].setKor(0);
stArray[index].setEng(0);
stArray[index].setMath(0);
System.out.println("학생 데이터를 삭제했습니다.");
}
앞서 index를 왜 -1로 했는지에 대해 적었는데, 만약 -1이라는 숫자가 나온다면, 그런 학생 우리 학교에 없어요~ 해줘야겠죠? 그렇기 때문에 System.out.println("학생 데이터를 찾지 못했습니다.")를 출력해주고 더 이상 볼 일 없으니 그냥 바로 return해줍니다.. 근데 index가 -1이 아니고 우리가 찾고자 하는 학생을 실제로 찾았습니다. 그러면 그 학생의 이름과 성적 정보를 일단 먼저 출력해줍니다. 그리고 그 학생이 지니고 있던 이름과, 점수를.... 살포시 사라지게 합니다. name은 String형이기 때문에 ""로 만들어주고, 나머지 성적은 int형이기 때문에 다 0으로 만들어줍니다. 그러면 추후 학생을 모두 출력하는 과정에서 null값인 것들은 안 나오게 쳐내기 때문에 삭제된 학생들은 포함되지 않게 됩니다. 마무리로는 잘 삭제했다는 문구를 넣어줍니다.
3. 학생 정보 수정
//TODO 갱신 U update
public void update() {
System.out.print("수정할 학생 이름 : " );
String name = sc.next();
int index = search(name);
if(index == -1) {
System.out.println("학생 데이터를 찾지 못했습니다.");
return;
}
System.out.print("국어 : ");
int kor = sc.nextInt();
System.out.print("영어 : ");
int eng = sc.nextInt();
System.out.print("수학 : ");
int math = sc.nextInt();
stArray[index].setKor(kor);
stArray[index].setEng(eng);
stArray[index].setMath(math);
System.out.println("학생 데이터를 수정하였습니다.");
}
수정은 삭제와 거의 비슷합니다. 다만 삭제에서는 가차없이 null값을 만들어버리면 그만이었는데 이번에는 우리가 하나하나 수정할 값을 넣어줘야 한다는 차이가 있겠습니다.
4. 학생 정보 전체 출력
//TODO : 모두 출력
public void allPrint() {
for(int i=0;i<stArray.length;i++) {
if(stArray[i]!=null&&!stArray[i].getName().equals("")) {
StudentDto dto = stArray[i];
System.out.println(dto.toString());
}
}
}
그동안 열심히 입력한 값을 한 번에 다 보고 싶습니다. allPrint함수를 만들어줍니다. 마찬가지로 모두! 출력해야 하기 때문에 배열을 하나도 빠짐없이 훑어봐야겠습니다. 다만 삭제되거나 이상한 값이 포함되지 않도록 if조건문을 잘 써줘야합니다. 첫번째 조건은 stArray[i] != null입니다. 배열에 null값이 없다는 것인데, 이는 아까 삭제에서 null값으로 만들어버렸던 과정과 결부하여 값이 없는 학생은 꺼내오지마! 하는 것이죠. 두번째로는 stArray[i].getName().equals("")입니다. 이 역시 마찬가지로 이름이 없고 ""로 되어 있는 학생은 사양합니다~ 이런거죠. 이름도 모르는데 무슨 쓸모가 있겠어요? 이 두 조건을 만족했을 때 비로소 StudentDto dto = stArray[i];로 받아들여주고, 이를 다시 출력하기 위해서는 for문을 사용할 수도 있겠지만 편리하게 배열을 string형으로 보여주는 toString()을 사용하였습니다.
<Data Transfer Object>
package java0517;
//Data Transfer Object, 계층간 데이터 교환을 위한 객체
//로직을 갖고 있지 않는 순수한 데이터 객체이며, getter/setter 메서드만을 갖는다.
public class StudentDto {
private String name;
private int kor;
private int eng;
private int math;
public StudentDto() {
}
public StudentDto(String name, int kor, int eng, int math) {
this.name = name;
this.kor = kor;
this.eng = eng;
this.math = math;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getKor() {
return kor;
}
public void setKor(int kor) {
this.kor = kor;
}
public int getEng() {
return eng;
}
public void setEng(int eng) {
this.eng = eng;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
@Override
public String toString() {
return "StudentDto [name=" + name + ", kor=" + kor + ", eng=" + eng + ", math=" + math + "]";
}
}
DTO는 없어서는 중요한 부분입니다. 데이터가 왔다가 갔다가 할 수 있도록 도와주는 것인데요, setter/getter 함수로 이뤄져있다는 특징이 있습니다. 먼저 클래스를 생성하고 그 안에 private으로 정보의 값들을 선언합니다. 이렇게 값을 숨겨놨기 때문에 값들을 수정하기 위해서는 getter와 setter가 필수적으로 활용됩니다. 사실 이 기능은 이클립스에서도 클릭 두 번으로 손 쉽게 할 수 있는 것이기도 합니다. 다만 눈 여겨봐야 할 부분은 마지막 public String toString()입니다. 우리가 앞에서 allPrint함수를 쓸 때 for문을 사용하지 않고 dto.toString()을 써서 쉽게 출력했던 것을 기억할것입니다. 이것이 바로 그 속부분을 보여주는 것이라고 할 수 있겠습니다. 물론 이것을 적지 않아도 출력은 제대로 됩니다. 다만 마지막 함수를 통해서 toString함수를 썼을 때 어떠한 구조를 만들어주는구나~ 확인이 가능하겠습니다