2022-01-14 간단한 기초 포인트 관리 프로그램

GGAE99·2022년 1월 14일
1

진도

목록 보기
8/43

여태 객체의 개념과 기능에 대해 공부했는데, 이번에는 그 개념들을 사용해서 간단한 프로그램을 만들어본다! 여러분들이 보기에는 굉장히 기초 수준이라고 느낄 것 같아서 좀 부끄럽다...! 그래도 이 사람 코린이구나 하고 넘어가주셨으면 좋겠다!!

회원 등록, 수정, 삭제, 조회 등의 기능이 있는 프로그램을 만들어보겠다!
클래스의 배열을 통해 회원 정보를 정리할 계획이다.
넣을 정보는 회원의 등급, 이름, 가지고있는 포인트, 그리고 등급에 따른 보너스 포인트다.
등급은 silver gold vip vvip 이 4개로 나눌 것 이다.
배운 개념을 써먹기위해, 일단 들어가야할 정보를 다 가지고있는 상속 클래스를 만들어보자.

public abstract class Grade {
	private String grade;
	private String name;
	private int point;
    }

일단 상속 클래스를 만들었다. 등급에 따라 클래스를 나눌거기 때문에, Grade로 상속 클래스 이름을 지었다. 그리고 등급을 넣을 문자열 변수 grade와 name, 포인트 값을 넣을 정수형 변수 point를 선언해주었다.

변수의 값을 초기화해줄 기본 생성자와, 값을 넣어줄 매개변수 생성자를 만들어보자.

public Grade() {
		
	}
public Grade(String grade, String name, int point) {
		super();
		this.grade = grade;
		this.name = name;
		this.point = point;
	}

생성자 코드를 작성함으로, 변수의 값을 기본값으로 초기화해주고, 타 클래스에서 변수값을 매개변수를 통해 변환할 수 있도록 매개변수 생성자도 만들어주었다.

모든 변수를 private로 선언해주었기 때문에, 다른 클래스에서 이 변수들에 접근하기 위해서는 접근을 허용하는 메소드가 필요하다. getter setter메소드를 만들어보자.

public String getGrade() {//grade값을 가져오는 메소드
		return grade;
	}
	public void setGrade(String grade) {//grade 값을 변환하는 메소드
		this.grade = grade;
	}
	public String getName() {//name값을 가져오는 메소드
		return name;
	}
	public void setName(String name) {//name값을 변환하는 메소드
		this.name = name;
	}
	public int getPoint() {//point값을 가져오는 메소드
		return point;
	}
	public void setPoint(int point) {//point값을 변환하는 메소드
		this.point = point;
	}

getter setter 메소드를 통해 다른 클래스에서도 Grade클래스 변수에 접근할 수 있도록 했다.

그리고 클래스를 보면 abstract키워드가 선언되어있는데, 그 이유는 이 클래스가 추상 클래스이기 때문이다. 필자는 각 등급의 클래스에서 보너스의 값을 설정하는 메소드를 각각 설정하도록 만들기 위해서, getBonus메소드를 추상 메소드로 만들 것 이다.

public abstract double getBonus();

이제 getBonus메소드를 사용하려는 자식 클래스들은, Override를 통해 클래스를 수정해주어야 할 것이다. 등급별로 클래스를 만들어보자.

public class Silver extends Grade {	
	public Silver() {
		super();		
	}	
	public Silver(String grade, String name, int point) {
		super(grade, name, point);		
	}
	@Override
	public double getBonus() {
		return getPoint()*0.02;
	}	
}
-----------------------------------------------------------------------
public class Gold extends Grade{
	public Gold() {
		super();		
	}
	public Gold(String grade, String name, int point) {
		super(grade, name, point);
	}
	@Override
	public double getBonus() {
		return getPoint()*0.05;
	}
}
-----------------------------------------------------------------------
public class Vip extends Grade{
	public Vip() {
		super();		
	}
	public Vip(String grade, String name, int point) {
		super(grade, name, point);		
	}
	@Override
	public double getBonus() {
		return getPoint()*0.07;
	}
}
-----------------------------------------------------------------------
public class Vvip extends Grade{
	public Vvip() {
		super();
	}
	public Vvip(String grade, String name, int point) {
		super(grade, name, point);
	}
	@Override
	public double getBonus() {
		return getPoint()*0.1;
	}
}

Silver, Gold, Vip, Vvip 클래스를 만들어 모두 Grade클래스를 상속시키고, getBonus메소드를 각각의 클래스에서 수정해서 사용하도록 만들었다.
등급마다의 차이점은 포인트로 얻는 보너스 보인트의 값뿐이기 때문에, Grade클래스에 들어있던 생성자를 super()메소드를 통해 그대로 불러왔다.
@Override를 사용해서, 추상 메소드였던 getBonus를 각각의 클래스에서 용도에 맞게 수정하였다. Silver등급은 포인트의 0.02배를, gold등급은 0.05배, vip는 0.07배, vvip는 0.1배의 포인트 보너스를 각각 얻도록 설정하였다.

이제 사용할 변수와 클래스의 설정은 모두 끝났다. 실행 시킬 클래스와 직접 코드를 짤 구현 클래스를 만들어서 실행 클래스에 import해주겠다. 구현 클래스 이름은 PointMgr이다.

import kh.java.controller.PointMgr;

public class Start {
	public static void main(String[] args) {
	PointMgr pm = new PointMgr();
	pm.main();
	}
}

PointMgr의 메인 메소드를 작성한다. 이제 프로그램을 제대로 구현해보자!
우선 메인 메소드를 만들기 위해 필요한 클래스를 전부 넣어준다.

import java.util.Scanner;
import kh.java.vo.Gold;
import kh.java.vo.Grade;
import kh.java.vo.Silver;
import kh.java.vo.Vip;
import kh.java.vo.Vvip;

이제 메인 메소드를 선언한다.

public class PointMgr extends Grade {	
	
	@Override
	public double getBonus() {
		// TODO Auto-generated method stub
		return 0;
	}

	private Grade[] members;//Grade클래스 형 배열인 members
	private Scanner sc;//Scaaner 기능
	private int index;//회원의 입력수를 조절할 변수
	
	public PointMgr() {
		super();
		members  = new Grade[40];//40개의 값을 가진 Grade형 배열 객체를 members라고 선언한다.
		sc = new Scanner(System.in);//Scaaner 기능을 사용하기 위해 객체를 선언
		index = 0; //회원의 입력수를 조절할 변수
	}

추상 클래스를 상속해왔기 때문에, 그 클래스에 있는 추상 메소드를 @Override를 통해 수정해주지 않으면 프로그램이 정상적으로 구동되지 않는다.
Grade클래스에 있는 추상 메소드 getBonus();를 기본값을 반환하는 메소드로 만들어준다.

이제 메인 메소드에서 프로그램을 구현하는데 필요한 자원들을 만들어준다.

private Grade[] members;//Grade클래스 형 배열인 members
private Scanner sc;//Scaaner 기능
private int index;//회원의 입력수를 조절할 변수

위와 같은 변수를 선언한 것을 볼 수 있다. 다음 코드로 기본생성자를 통해서 값을 초기화해준다.

public PointMgr() {
super();
members = new Grade[40];//40개의 값을 가진 Grade형 배열 객체를 members라고 선언한다.
sc = new Scanner(System.in);//Scaaner 기능을 사용하기 위해 객체를 선언
index = 0; //회원의 입력수를 조절할 변수

members배열은 40개의 Grade클래스 형의 배열로, 회원수를 조절할 index 변수는 0으로 초기화했다.

프로그램의 메인메뉴를 만들어보자.

public void main() {//프로그램 실행
		while(true) {
			System.out.println("===== 포인트 괸리 프로그램 v5 =====");
			System.out.println("1. 회원 정보 등록");
			System.out.println("2 전체 회원 조회");
			System.out.println("3. 회원 1명 조회");
			System.out.println("4. 회원 정보 수정");
			System.out.println("5. 회원 삭제");
			System.out.println("0. 프로그램 종료");
			System.out.print("선택 > ");
			int sel = sc.nextInt();

1~5번까지의 메뉴와, 프로그램 종료 메뉴를 선택핼 수 있도록 만들었다.
사용자가 번호를 입력하면 각각의 메소드를 실행해 원하는 메소드를 실행할 것 이다.

			switch(sel) {
			case 1:
				insertMembers();
				break;//case 1 end
			case 2:
				printAllMembers();
				break;//case 2 end
			case 3:
				printOneMember();
				break;//case 3 end
			case 4:
				updateMember();
				break;//case 4 end
			case 5:
				deleteMember();
				break;//case 5 end
			case 0:
				System.out.println("프로그램을 종료합니다.");
				return;//case 0 end
			default:
				System.out.println("잘못된 입력을 행하셨습니다.");
				break;//break end
			
			}//switch end

switch문을 사용해, 사용자가 선택한 번호에 부합하는 메소드를 넣어주었다. 프로그램을 종료하는 0번은, case의 마지막에 break;가 아닌 return을 넣어주어 메소드를 종료하고 메뉴를 빠져나오도록 만들었다. 다른 번호를 선택했을시, 메소드가 끝나고 break를 만나 while문으로 돌아간다.

이제는 실행시킬 메소드를 하나씩 작성해나가는 일만 남았다.
1번 회원 정보 입력 메소드부터 입력해보자.

public void insertMembers() {
		if(index==40) {
			System.out.println("저장 공간이 다 찼습니다.");
		}else {
		System.out.println("===== 회원 정보 등록 =====");
		System.out.print("회원 등급 입력[silver/gold/vip/vvip] : ");
		String grade = sc.next();
		System.out.print("회원 이름 입력 : ");
		String name = sc.next();
		System.out.print("회원 포인트 입력 : ");
		int point = sc.nextInt();
		switch(grade) {//등급에 따라 대입값을 다르게 하기위한 switch문
		case "silver":
			members[index++]=new Silver(grade, name, point);
			break;
		case "gold":
			members[index++]=new Gold(grade, name, point);
			break;
		case "vip":
			members[index++]=new Vip(grade, name, point);
			break;
		case "vvip":
			members[index++]=new Vvip(grade, name, point);
			break;
		default:
			System.out.println("잘못된 입력을 하셨습니다.");
			return;
		}				
		System.out.println("회원 정보가 등록되었습니다!");
		}
	}//insertMembers end

회원 수를 관리하는 index가 40까지 차있으면, members[]배열이 40번째 배열까지 전부 다 차있는 것 이기 때문에, 저장 공간이 없다는 출력과 함께 다른 코드와 만나지않고 메소드를 끝마친다.
저장공간이 다 차있지 않은 경우, 새로운 회원의 등급, 이름, 소유한 포인트를 입력받는다.
아까 말했듯이 우리는 등급에 따라 얻을 수 있는 보너스 포인트를 달리할 것 이다. 그러므로, 입력받은 등급 값 grade변수가 어떤거냐에 따라 상황을 나누어 올바른 객체에 값을 집어넣는다.

이 부분이 중요하다고 생각한다. 다향성의 성질을 이용해 Grade클래스형의 배열인 members[]를 각 등급의 클래스 객체로 생성한다. 이와 같은 일이 가능한 이유는 모든 등급 클래스가 Grade클래스의 상속을 받았기 때문이다. 알기쉽게 코드로 풀어써보자.

case "gold":
	Grade g = new Gold(grade, name, point);
	members[index]=g;
       	index++;
	break;

Grade형태의 g를 Gold 객체로 만들어낸다. 아까도 말했듯이 이게 다향성이다. Grade클래스에는 매개변수 생성자가 이미 생성되어있고, 그 생성자를 이용해 Grade클래스의 grade, name, point변수에 사용자가 입력한 값을 넣는 것 이다.
그리고 Grade클래스에는 추상 메소드가 있었는데, 바로 getBonus()메소드이다.
이 메소드값을 각 등급에 맞게 처리하기위해 우리는 각 클래스를 생성한 것 이다. Grade클래스의 point변수에는 사용자가 입력한 값이 들어가있고, Grade클래스는 추상 메소드를 @Override를 통해 수정한 Gold객체의 getBonus()메소드로 실행한다. Bonus값에 point*0.05를 해주는 것 이다. 이 원리를 제대로 설명하기 힘든 내 어휘력이 굉장히 밉다...

그 후, members[index]값에 객체를 저장한다. index값을 처음에 0으로 설정한 이유는, index가 배열의 순서를 알리는 값이기 때문이었다. 처음 이 메소드를 실행하면 member[0]값이 채워진다. 그러면 다음 회원 정보를 입력할 때는 이 객체에 다시 회원 정보를 저장하면 안되기때문에, index값을 1 늘려주어서 정보를 중복해서 받는 객체가 없게끔 만들어준다.
위의 과정을 짧은 코드로 만들면 이렇게 되는 것 이다.

members[index++]=new Gold(grade, name, point);

두번째, 전체 회원을 조회하는 메소드를 만들어보자.

public void printAllMembers() {
		System.out.println("===== 전체 회원 출력 =====");
		if(index == 0) {
			System.out.println("회원이 존재하지 않습니다.");
		}else {
			System.out.println("등급\t이름\t포인트\t보너스");
			System.out.println("-----------------------------");
			for(int i = 0;i<index;i++) {
				System.out.printf("%s\t%s\t%d\t%.2f\n",members[i].getGrade(),members[i].getName(),members[i].getPoint(),members[i].getBonus());
			}
		}//if end
	}//printAllMembers end

두번째 메소드는 비교적 짧은데, 그 이유는 그냥 for문써서 i값부터 index값 전까지 모든 members[i]값을 출력해주면 끝나기 때문이다. 주의할 점은, 회원정보가 아무것도 입력되지 않았을 때 이다. 어떤 회원 정보도 입력하지 않았다면, index값은 0일 것 이므로, if(index==0)의 조건을 걸어 입력된 회원이 존재하지 않는 경우 출력하는 코드도 만들어준다.

그럼 바로 세번째 메소드다. 세번째부터 다섯번째, 즉 마지막 메소드까지는 특정 회원의 이름을 입력해서 그 회원을 찾아내 동작하도록 만들어진다. 그러므로 일단 사람을 찾는 메소드를 만들어본다.

public int searchIndex(String searchName) {
		for(int i=0; i<index; i++) {
			if(members[i].getName().equals(searchName)) {
				return i;
			}
		}
		return -1;
	}//searchIndex end

이게 사람을 찾는 메소드이다. 정수값을 반환하는 메소드다. 우리는 이름을 입력해서 일치하는 사람이 있는지 찾고싶으니, 입력받을 매개변수를 String값으로 지정해놓는다. 지금까지 입력받은 모든 회원 정보와 비교해서 members[i].getName의 값이 searchName과 같다면 그 즉시 같은 이름이 있었던 i번째 배열의 i 값을 반환하고 메소드를 멈춘다.
i값을 반환하는 이유는, 우리가 이 메소드를 짠 이유가 members[]배열의 몇번째가 우리가 원하는 값인지 알고싶어서 짠거기 때문이다. i값은 그 값을 알려주는거다!

members[i].getName()//의 값이 searchName과 같다면
//우리는 members[i]값을 출력해주고싶은 것 이다.
//그러므로 우리는 i값을 리턴받는다.

만약 회원 중 같은 이름이 없다면, 메소드는 -1값을 반환한다. 왜 -1로 설정했냐면, index는 0부터 점점 커져가도록 코드가 짜져있기때문에, -값에 도달할 일은 절대 없기 때문이다.

일치하는 사람을 찾는 메소드를 작성했으니 세번째 메소드도 만들어보자.

public void printOneMember() {
		System.out.println("===== 회원 정보 조회 =====");
		if(index==0) {//회원 정보가 아무것도 없을 때
			System.out.println("등록된 회원 정보가 없어 조회가 불가는합니다.");
		}else {
			System.out.print("조회하고 싶은 회원 이름 입력 : ");
			String searchName = sc.next();
			int searchIndex = searchIndex(searchName);
			
			if(searchIndex==-1) {//일치하는 이름이 없을 때
				System.out.println("일치하는 회원을 조회할 수 없습니다.");
			}else {
				System.out.println("등급 : "+members[searchIndex].getGrade());
				System.out.println("이름 : "+members[searchIndex].getName());
				System.out.println("포인트 : "+members[searchIndex].getPoint());
				System.out.printf("보너스 : %.2f\n",members[searchIndex].getBonus());
				
			}//inner if end
		}//if end
	}//printOneMember end

조회하고 싶은 이름부터 사용자한테 입력받는다. 그리고 방금 생성한 searchIndex메소드를 사용해, 같은 이름을 가진 사람이 몇번째 배열에 있는지 알아내고, 그 값을 출력해준다.
만약 일치하는 이름이 없어서 searchIndex값이 -1이 나왔다면, 일치하는 회원이 없다고 알려준다.

네번째 메소드는 수정하는 메소드이다.

public void updateMember() {
		System.out.println("===== 회원 정보 수정 =====");
		if(index==0) {//회원 정보가 아무것도 없을 때
			System.out.println("등록된 회원 정보가 없어 조회가 불가는합니다.");
		}else {
			System.out.print("정보 수정을 진행할 회원의 이름 입력 : ");
			String updateName = sc.next();
			int searchIndex = searchIndex(updateName);
			if(searchIndex==-1) {//일치하는 이름이 없을 때
				System.out.println("일치하는 회원을 조회할 수 없습니다.");
			}else {//회원 정보를 찾았을 때
				System.out.print("수정할 등급 입력 : ");
				String grade = sc.next();
				System.out.print("수정할 이름 입력 : ");
				String name = sc.next();
				System.out.print("수정할 포인트 입력 : ");
				int point = sc.nextInt();
				
				switch(grade) {//수정한 회원의 등급에 따라 보너스 포인트를 달리하기 위해 switch 문을 사용해 각각의 상황을 만들어준다.
				case "silver"://silver 등급으로 수정
					members[searchIndex]=new Silver(grade, name, point);
					break;
				case "gold"://gold 등급으로 수정
					members[searchIndex]=new Gold(grade, name, point);
					break;
				case "vip"://vip 등급으로 수정
					members[searchIndex]=new Vip(grade, name, point);
					break;
				case "vvip"://vvip 등급으로 수정
					members[searchIndex]=new Vvip(grade, name, point);
					break;
				default://옳지않은 등급 값을 입력했을 때
					System.out.println("잘못된 값을 입력하셨습니다. 아마도 등급을 잘못 입력하셨을 것 이라고 생각됩니다.");
					break;
				}//switch end
			}//inner if end
		}//if end
	}//updateMember end

이 메소드가 좀 긴데, 그 이유가 수정할때 등급도 같이 바뀌면 얻는 보너스 포인트도 달라져야하기 때문이다. 아까와 같은 방식으로 index==0일때는 사람이 없다고 말해주고, 아니라면 수정을 원하는 회원의 이름을 입력해달라고 출력한다. 또다시 searchIndex메소드이다.
이 메소드를 사용해 사용자가 입력한 이름과 같은 name값이 있는 배열이 있나 찾아보고, 없다면 없다고 말하고 메소드를 나간다.
일치하는 이름이 있다면, 배열의 값을 등급부터 다시 입력받아 정보를 수정해준다. 이 과정에서 해주어야할게 바로 객체의 클래스를 변경하는 것 이다. silver등급이었던 회원이 vip등급으로 오른다면, 소유하고있던 포인트 또한 vip등급의 혜택에 맞게 적용되어야할 것 이다.
그래서 switch문을 사용해, 1번 메소드처럼 각 등급별로 객체의 클래스를 다시 넣어주고, 값을 대입하는 것 이다. 원리는 1번 메소드와 같다.

마지막 삭제 메소드이다.

public void deleteMember() {
		System.out.println("===== 회원 정보 삭제 =====");
		if(index==0) {//회원 정보가 아무것도 없을 때
			System.out.println("등록된 회원 정보가 없어 조회가 불가는합니다.");
		}else {
			System.out.print("삭제를 진행할 회원의 이름 입력 : ");
			String deleteName = sc.next();
			int searchIndex = searchIndex(deleteName);
			if(searchIndex==-1) {
				System.out.println("회원 정보를 찾을 수 없습니다.");
			}else {
				for(int i = searchIndex; i<index-1;i++) {
					members[i]=members[i+1];
				}//for end
				members[--index]=null;//자리를 모두 옯겨주고 마지막으로 입력한 값을 비워주고, index 값을 1 줄여주는 역할
				System.out.println("삭제를 완료했습니다.");
			}//inner if end	
		}//if end
		
	}//deleteMember end

이 메소드 역시 회원 정보가 없을시, 정보가 없다고 출력 후 메소드를 빠져나간다.
있다면, 삭제하고싶은 회원의 이름을 입력받고, 또또 searchIndex메소드를 사용해서 값을 return받는다. return받을 값을 int형 변수에 집어넣고(int searchIndex), members[searchIndex]값을 삭제하는 것 이다.
하지만 만약 바로 값을 초기화한다면, searchIndex번재 배열에는 어떤 데이터도 들어갈 수 없게 설계가 되어있다. 그러므로 members[searchIndex]값을 초기화하는 것이 아닌, members[searchIndex+1]값을 members[searchIndex]값에 대입하는 것 이다. 그렇게 for문을 사용하여 [searchIndex]값부터 members[index]값 까지 모두 한칸씩 땡겨준다.
그 이후, 다시 새로운 데이터를 넣을 수 있도록 index값을 1 빼준다.
그렇게 하고, 마지막 입력받은 값을 삭제해줌 으로서, 데이터를 한개밖에 입력하지 않은 상황에서도 메소드가 성립하게 만들어준다.

굉장히 오랜시간 들여서 만들었다. 나도 다시한번 이해해보자고 시작했지만, 이렇게 오래 걸릴줄 몰랐다... 사실 글을 이렇게 주저리주저리 쓰는 것 보다는 사진같은 것을 이용해 설명하는 것이 더 이해도 잘되고 쉽겠지만, 시간이 지금보다 더 걸리고, 일단 내가 너무 코린이라 적절한 사진을 가져올 자신도 없다...

글로서라도 열심히 정리하고 설명하면서 보다 더 이해하기위해 노력하는 포스팅을 진행할 것 이다.
오늘은 포스팅을 2개나했다. 살짝 밀려서 그런건데, 뭐 공부는 다다익선 아니겠는가. 내일은 주말이니까 백준이나 열심히 풀어야겠다. 아주 기초적인 친구들부터.
오늘은 여기까지 하겠다. 안녀엉!

1개의 댓글

comment-user-thumbnail
2022년 4월 26일

안녕하세요 블로그내용을 볼수있게 게시해주셔서감사합니다 게시글내용을 보던중에궁금증이생겼는데 혹시 질문을해도될까요?

답글 달기