본문 바로가기

LECTURE/# java basic

[초급 JAVA]예제-생성자 - 로또 프로그램 만들기(lotto program)

반응형

생성자 - 로또 프로그램 만들기(lotto program)

By Commin November 21, 2016

안녕하세요. commin 입니다. 이번시간에는 저번시간까지 배웠던 내용을 바탕으로 간단한 로또 프로그램을 짜보겠습니다. 

로또프로그램을 만들려면 고려되어야 할 점들이 무엇이 있을까요?


  1. 1부터 45까지의 숫자중 랜덤으로 6개가 생성되어야 한다.
  2. 중복되는 수는 없어야 한다.
  3. 보너스숫자도 추첨되게 해야한다.
이정도면 지금 제가 만들려는 프로그램에는 충분할거 같습니다. 3가지의 고려할점을 잘 기억하고 프로그램을 만들어 봅시다.
일단 LottoMain.java 클래스파일과 LottoPro.java 클래스파일을 만듭니다.
LottoMain은 프로그램의 중심이 되는 소스입니다. 맞습니다. main 메서드를 이곳에 넣을 생각입니다. 예전 같았으면 지금 만들려고하는 로또 프로그램정도는 같은 클래스안에서 만들 수 있지만, 우리는 이제 그냥 초급이 아니기때문에, 프로그래밍의 골격에 맞춰서 소스를 짜는 연습을 해야합니다.
때문에 Main 메서드 에서는 필요한 클래스를 호출하고 프로그래밍이 순서대로 올바르게 작동 할 수 있도록 분기점을 코딩 하기로 하고, 나머지 기능적 요소들은 모두 LottoPro 에서 코딩하기로 합니다.  Main은 뇌의 역할을 , Pro는 뇌의 명령에따라 움직이는 역할(행동)로 나누어 코딩하는 겁니다.

LottoMain 소스 해석

소스를 열어 LottoMain 클래스를 살펴보면, 맨 밑에 하단에 메인메서드부터 봅시다.

1
2
3
public static void main(String[] ar) {
    new LottoMain();// 객체가 딱히 필요없을때는 인스턴스만 만들어줍니다.
}

cs

메인메서드를 찾는 것이 소스 읽기에 시작이 됩니다. 이전과는 다르게 메인메서드 안이 많이 심플해졌습니다. 

new LottoMain(); 한줄밖에 코딩하지않았습니다. 저번시간에 배운 생성자를 호출함으로써, 코딩을 시작합니다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public LottoMain() {
        // main 함수에서 LottoMain의 인스턴스를 호출하였으므로 제일 먼저 
//생성자인 이곳이 실행되게됩니다.
 
        // 생성자에서 필요한 변수를 초기화 합니다.
        in = new BufferedReader(new InputStreamReader(System.in));
        
        //주석을 순서대로 풀면서 결과를 확인 해 봅시다.
        program = new LottoPro(true);
//        program = new LottoPro(false);
//        program = new LottoPro();
 
        start();// 메인함수 내에서 실행되는것이 아닌, 
//메인함수가 생성한 자기자신의 인스턴스에 의해 실행되므로, 
//static 메서드가 아닌 start를 실행 할 수있습니다.
 
    }

cs

main 메서드에서 호출된 LottoMain의 생성자부분입니다. 이곳도 그렇게 많지않습니다. 입력을 받기위해 BufferedReader 를 초기화 해줬고, LottoPro 클래스를 객체화해줬습니다. 여기서 객체화 하는 법에 따라서 프로그램의 성질이 바뀝니다. 이것이 이번시간에가장 말씀드리고 싶었던 부분이며, 코딩에서도 유용한 방법이니 꼭 주석을 하나씩 풀면서 실행해보시길 바랍니다.

그리고 start() 메서드를 호출하면서 본격적으로 프로그램이 시작됩니다.

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
public void start() {
        // 이렇게 프로그램을 동작시킬 메서드를 하나 만들어서 관리하는것이 편합니다.
        System.out.println("로또 프로그램이 시작됩니다.");
        for (int i = 1; i < 11; i++) {
            System.out.println(i * 10 + "%");
            if (i == 10)
                System.out.println("환영합니다!!");
        }
        System.out.println();
        do {
            System.out.println("-----------MENU----------");
            System.out.println("1.추첨   2.보기   3.끝내기");
            System.out.println("-------------------------");
            System.out.print(">> ");
            try {
                this.command = Integer.parseInt(in.readLine());
                
            } catch (IOException e) {
                System.out.println("(System)IOException !! try again..");
                continue;
            }
            switch(this.command){
            case RECOMMANDATION_NUMBER: 
                program.start_recommandation();
                break;
            case PRINT_NUMBER: 
                program.printNumber();
                break;
            case EXIT: 
                System.out.println("(System)bye!");
                return;
            }
        } while (true);
 
    }

cs

start 메서드에서는 기본적인 문구를 출력하는 것 외에 입력받은 int형 데이터 값에 따라 분기되어 해당 기능을 실행 하고 이작업을 반복할 수 있도록 while 문과 switch문이 쓰였습니다.(do~while문은 안써도됩니다. 그냥 이런것도 있으니 기억하라는 의미에서 사용해봤습니다.) 간략히 순서를 설명하자면 메뉴가 콘솔창에 출력이되고 1,2,3 중에 입력받은 숫자를 switch문으로 분기하여 생성저에서 초기화 시켰던 LottoPro 객체를 사용하여 LottoPro에 정의되어있는 행동들(메서드)를 실행시키고 3번을 입력받기전까지 이과정을 반복합니다. 

전체소스를 봐도 느껴지지만, 메서드와 클레스를 여러개 사용하여 코딩하니 코딩의 숫자가 훨씬 줄어들고 코딩도 간결해져서 가독성도 확연히 좋아졌다는걸 느끼실겁니다.

LottoPro 소스 해석

1
2
3
4
5
6
7
8
9
public LottoPro() {
      System.out.println("(System)보너스추첨은 불가합니다.");
      can_bonus = false;
}
 
public LottoPro(boolean can_bonus) {
      System.out.println("(System)보너스추첨도 가능합니다.");
      this.can_bonus = can_bonus;
}

cs

LottoPro 의 생성자 부분을 먼저 보자면, 하나는 아무런 인자도 받지않은 default 형 생성자이며 다른 하나는 boolean 값을 인자로받는 생성자입니다. 

여기서 저번시간에 말하지 않았던게 있는데, 생성자를 아무것도 코딩하지않으면 자바는 그냥 기본형인 인자없는 형태의 생성자를 실행합니다. 그러나 인자를 가진 생성자를 하나 코딩했다면, 자바는 더이상 자동으로 default형 생성자를 호출하지않고, 사용하려면 직접 코딩해줘야합니다. 무조건 인자를 받고 객체를 생성하게 하고싶다면 인자가없는 생성자메서드를 만들지않으면 됩니다. 

쨌든 , 인자가없는 생성자를 이용하여 만들어진 객체는 무조건  can_bonus 값이 false 이므로 보너스 숫자가 없이 6개의 숫자만 출력되게됩니다. 

can_bonus 를 인자로 받은 객체는 can_bonus의 값에따라 True이면 보너스 숫자가 포함되고 false 이면 보너스 숫자가 포함되지 않은.. (쓰다보니까 System.out.println 안에 문구가 true or false 에 따라 다르게 출력되게 해줘야 되겠네요? 여러분이 고쳐주세요 ^^; ) 

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
    private int getBounusNumber(int[] normal_number) {
        int bonus = 0;
        int imsi = 0;
 
        while (true) {
            imsi = (int) (Math.random() * 45 + 1);
            for (int j = 0; j < normal_number.length; j++) {
                if (normal_number[j] == imsi) {
                    duplicate = true;
                }
            }
            if (duplicate) {
                duplicate = false;
                continue;
            } else {
                break;
            }
        }
        bonus = imsi;
        System.out.println("(System)Success Create Bonus Number");
        return bonus;
    }
 
    private int[] getNormalNumber() {
        int[] normal_number = new int[6];
        int imsi = 0;
        for (int i = 0; i < normal_number.length; i++) {
            imsi = (int) (Math.random() * 45 + 1);// random 함수는 
//0.0~ 0.999...까지 랜덤으로 난수를 생성하는 함수입니다.
 
            for (int j = 0; j < i; j++) {
                if (normal_number[j] == imsi) {
                    duplicate = true;
                }
            }
            if (!duplicate) {
                normal_number[i] = imsi;
            } else {
                duplicate = false;
                i--;
            }
 
        }
        System.out.println("(System)Success Create Normal Number");
 
        return normal_number;
    }

s

하단에 getBonusNumber와 getNormalNumber 는 private 로 선언된 메서드 들입니다. 아직 접근제한자를 설명하지않았지만 지금까지 포스팅을 보신분들은 대충 감을 가지고 있을겁니다. private 로 선언된 메서드나 변수는 다른 클래스에서 객체를 생성하고 접근하여 사용하려 해도 사용할 수가 없습니다. private는 가장 폐쇄적인 성격을 가진 접근제한자이기때문에 같은 폴더 같은 파일 같은 클래스안에서만 접근을 허가하는 놈입니다. 때문에 저는

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    public int[] start_recommandation() {
        
        if (can_bonus) {
            numbers = new int[7];
        } else {
            numbers = new int[6];
        }
        int [] imsi=getNormalNumber();//로또번호 6개생성
        for (int i = 0; i < numbers.length; i++) {
            if (i == 6) {
                numbers[i] =getBounusNumber(numbers);//만약 보너스번호가있다면
//보너스번호 생성
            } else {
                numbers[i] = imsi[i];
            }
        }
        
        return numbers;
 
    }
cs

public 으로 선언된 start_recommandtaion 메서드를 사용하여 getNormalNumber 나 getBonusNumber 메서드를 사용 했습니다. public 은 private와 정반대로 같은 프로그램이라면 접근을 허가하는 개방적인 놈이기때문에 쉽게 접근 할 수있고, 그안으로 들어가면 private 로 선언된 놈들도 어차피 같은 class 이므로 사용이 가능해 지는것이죠. 이건 다음 포스팅에서 그림으로 그려서 쉽게 설명 하겠습니다. 쨌든 이렇게 해서 저는 1번이 눌렸을때 start_recommandation 메서드를 하나만 호출하면, 이곳에서 알아서 중복체크도 하고~ 보너스도 출력할것인지도 판단해서 같이 배열에다가 넘겨 줍니다.

어려운 부분은 없다고 느끼지만, getBonusNumber메서드에서 인자로 numbers 라는 6개의 로또번호가 저장된 배열을 받는 것은 보너스 번호가 기존에 6개의 숫자와 겹치는것을 방지하기위해 또한번 중복체크를 하기위함입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    public void printNumber(){
        if(numbers==null||numbers[0]==0){
            System.out.println("(System)Empty Value");
            return;
        }
        for(int i=0;i<numbers.length;i++){
            if(i==6){
                System.out.println("(BONUS)>>>"+numbers[i]);
            }else{
            System.out.println(">>>"+numbers[i]);
            }
        }
        
        
    }

cs

printNumber를 호출하게되면, 배열의 첫번째부터 여섯번째 까지의 숫자는 당연히 정상적인 로또 번호이고 만약 배열의 길이가 7 이라면 보너스번호가 있는 것으로 간주되어 마지막에 보너스번호를 출력해줍니다.





반응형