[코테공부] 빙고 만들기 - 자바 클래스 설계 방법론
[코테공부] 빙고 만들기 - 자바 클래스 설계 방법론 — #코테공부 #빙고만들기 #자바클래스 #개발자의도구들 AI스쿨 msa기반 java 백엔드 코스 중에 공부한 내용...
#코테공부 #빙고만들기 #자바클래스 #개발자의도구들
AI스쿨 msa기반 java 백엔드 코스 중에 공부한 내용을 작성하였습니다.
\본 게시글은 PC버전에 최적화되어있습니다.\
빙고
자바 연습문제
문제
| 💡 빙고 게임은 다음과 같은 방식으로 이루어진다.먼저 아래와 같이 25개의 칸으로 이루어진 빙고판에 1부터 25까지 자연수를 한 칸에 하나씩 쓴다 |
|---|
| 다음은 사회자가 부르는 수를 차례로 지워나간다. 예를 들어 5, 10, 7이 불렸다면 이 세 수를 지운 뒤 빙고판의 모습은 다음과 같다. |
|---|
| 차례로 수를 지워가다가 같은 가로줄, 세로줄 또는 대각선 위에 있는 5개의 모든 수가 지워지는 경우 그 줄에 선을 긋는다. |
|---|
| 이러한 선이 세 개 이상 그어지는 순간 "빙고"라고 외치는데, 가장 먼저 외치는 사람이 게임의 승자가 된다. |
|---|
| 철수는 친구들과 빙고 게임을 하고 있다. 철수가 빙고판에 쓴 수들과 사회자가 부르는 수의 순서가 주어질 때, 사회자가 몇 번째 수를 부른 후 철수가 "빙고"를 외치게 되는지를 출력하는 프로그램을 작성하시오. |
|---|
문제풀이
빙고를 어떻게 판단할 것인가?
입력을 리스트로 받아 \[\[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\]\] 형태로 저장한 다음 사회자가 불러주는 숫자를 체크하여 빙고 판별을 합니다.
빙고는 열빙고, 행빙고, 대각선 빙고, 역대각선 빙고가 있으므로 각각에 해당하는 배열을 만들어 빙고 체크 여부를 결정합니다
열빙고, 행빙고, 대각선 빙고, 역대각선 빙고 카운트가 3이면 모든 행동을 중지하고 사회자가 부른 숫자가 몇번째인지 출력합니다.
BingoBoard() 클래스와 Main()클래스만을 사용하여 빙고를 구현하겠습니다.
각 클래스에 구현될 기능들
클래스 설계를 중점적으로
우선 자바의 Main()클래스에 들어갈 기능을 선별합니다.
| 사용자로부터 빙고판을 입력받는다.사회자 번호를 입력받는다.빙고판에서 사회자 번호를 체크한다.빙고판의 빙고를 체크한다.빙고를 카운트한다. -> 3개이상이면 몇번째 숫자인지 출력한다. |
|---|
이 기능외에는 모두 BingoBoard()클래스에서 구현하고, Main()클래스 내에서는 함수 호출과 변수만 전달하도록 구현하겠습니다.
Main()클래스에서 구현하는 기능과 BingoBoard()에서 구현하는 기능들이 어떤 차이가 있는지 살펴보며 코드를 읽으시면 좋을 것 같습니다.
Main()클래스의 기능 위주로 코드를 작성했습니다. 외의 기능들은 그때 그때 BingoBoard()클래스의 메서드에 구현하여 코드로 작성하였습니다.
1. 사용자로부터 빙고판을 입력받는다.
내부에서 \[\[num, num, ...\]. \[num, num, ...\]\] 형태의 빙고판을 만들어야 하기 때문에 이중 배열을 내부적으로 만들어 줘야합니다.
저는 input()함수를 따로 만들어 주어 빙고판을 구성하였습니다.
import java.util.\; public class Main { private static ArrayList<ArrayList<Integer>> input(Scanner scanner){ ArrayList<ArrayList<Integer>> board = new ArrayList<>(); for (int i = 0; i < 5; i){ ArrayList<Integer> row = new ArrayList<>(); // \[\[\] = row = i\], row\[1, 2, 3, 4, 5 = col = j\] for (int j = 0; j < 5; j){ row.add(scanner.nextInt()); } board.add(row); } return board; }public static void main(String\[\] args) { Scanner scanner = new Scanner(System.in); ArrayList<ArrayList<Integer>> board = input(scanner); System.out*.println("board:" + board); }} |
|---|
main()함수에 board를 호출하면 input메서드에서 board를 생성하여 반환하여 줍니다. 입력값은 총 25번으로 하나씩 숫자를 입력하면 됩니다.
파이썬 처럼 이중 리스트를 만드는 일조차 간단하지 않기 떄문에, 여러번 시도해보며 이중 리스트를 만드는것에 익숙해질 필요가 있을 것 같습니다.
2. 입력받은 빙고판 매핑
BingoBoard()클래스에 입력된 빙고판의 모든 숫자를 row, column위치를 계산하여 따로 배열로 저장해둡니다.
import java.util.\*;public class BingoBaord { private int MaxNumberCount = 25; private ArrayList<ArrayLis<Integer>t> bingoBoard; private int\[\] rowMap; private int\[\] columnMap;BingoBaord(ArrayList<ArrayList<Integer>> bingoBoard){ this.bingoBoard = bingoBoard; rowMap = new int\[MaxNumberCount + 1\]; columnMap = new int\[MaxNumberCount + 1\]; } public void makeNumberMapping(){ for (int i = 0; i < 5; i){ for(int j = 0; j < 5; j){ int number= (int) bingoBoard.get(i).get(j); rowMap\[number\] = i; columnMap\[number\] = j; } } }} |
|---|
위치를 계산은 빙고의 핵심이 되는 기능입니다. 이를 통해 행,열, 대각선, 역대각선 빙고를 수월하게 계산할 수 있습니다.
3. 사회자 숫자 입력
사회자의 입력값은 반복문을 통해 받습니다. 이때 반복문의 초기 변수인 index는 몇 번째 숫자에 빙고가 나왔는지 체크하는데 사용합니다.
public static void main(String\[\] args) { Scanner scanner = new Scanner(System.in); ArrayList<ArrayList<Integer>> board = input(scanner); System.out.println("board:" + board); for(int index = 1; index <26; index++ ) int moderatorNumber = scanner.nextInt(); //moderatorNumber 빙고판에서 체크 // 행, 열, 대각선, 역대각선 빙고를 체크한다. // 빙고갯수가 3개인지 체크한다. // 빙고가 3개면 반복문 종료하고 index를 출력한다. } } |
|---|
moderatorNumber은 입력됨에 따라 BingoBaord클래스의 매서드의 인자로 들어가 숫자를 체크합니다. 체크된 숫자는 빙고(열, 행, 대각선, 역대각선)를 체크하며, 빙고가 3개이상 등장하면 index를 출력하고 반복문을 종료하게끔 구현합니다. 코드에 해당 기능들을 주석으로 간략하게 작성하였습니다.
4. 숫자체크, 빙고체크, 빙고 카운트
BingoBoard에서 해당 기능들을 만들어줍니다. 이후 Main()클래스는 BingoBoard의 기능들을 호출만 합니다.
4.1 숫자체크
사회자 숫자가 빙고판의 몇 행 몇 열인지 받아온 후, 따로 배열로 저장합니다. 2. 입력받은 빙고판 매핑을 통해 숫자의 위치를 손쉽게 얻어 카운트할 수 있습니다.
카운트는 행(배열),열(배열), 대각선, 역대각선 각각 따로 저장해둡니다.
행 = \[1행 숫자 갯수, 2행 숫자 갯수, 3행 숫자갯수, 4행 숫자 갯수, 5행 숫자 갯수\]
열 = \[1열 숫자 갯수, 2열 숫자 갯수, 3열 숫자갯수, 4열 숫자 갯수, 5열 숫자 갯수\]
public class BingoBaord { private int MaxNumberCount = 25; private ArrayList<ArrayList<Integer>> bingoBoard; private int\[\] rowMap; private int\[\] columnMap; // 추가로 선언되는 변수들 private int\[\] rowCount; private int\[\] columnCount; private int diagonal; private int antidiagonal; BingoBaord(ArrayList<ArrayLis<Integer>t> bingoBoard){ this.bingoBoard = bingoBoard; rowMap = new int\[MaxNumberCount + 1\]; columnMap = new int\[MaxNumberCount + 1\]; } public void makeNumberMapping(){ for (int i = 0; i < 5; i){ for(int j = 0; j < 5; j){ int number= (int) bingoBoard.get(i).get(j); rowMap\[number\] = i; columnMap\[number\] = j; } } }} |
|---|
get() 매서드를 따로 만들어 열과, 행정보를 받아옵니다.
4.2 빙고체크
mark()메서드를 구현하여 가져온 매핑 위치를 배열로 체크합니다.
public class BingoBaord { private int MaxNumberCount = 25; private ArrayList<ArrayList<Integer>> bingoBoard; private int\[\] rowMap; private int\[\] columnMap; // 추가로 선언되는 변수들 private int\[\] rowCount; private int\[\] columnCount; private int diagonal; private int antidiagonal; BingoBaord(ArrayList<ArrayLis<Integer>t> bingoBoard){ this.bingoBoard = bingoBoard; rowMap = new int\[MaxNumberCount + 1\]; columnMap = new int\[MaxNumberCount + 1\]; } public void makeNumberMapping(){ for (int i = 0; i < 5; i){ for(int j = 0; j < 5; j){ int number= (int) bingoBoard.get(i).get(j); rowMap\[number\] = i; columnMap\[number\] = j; } } public void mark(int modertorNumber){ int rowPosition = rowMap\[modertorNumber\]; int colPosition = rowMap\[modertorNumber\]; rowCount\[rowPosition\]; columnCount\[colPosition\]; if (rowPosition == colPosition){ antidiagonal++; } if (rowPosition + colPosition == 4){ diagonal++; } }} |
|---|
mark()메서드를 통해 행과, 열을 가져온 후 각각의 Count배열에 넣어줍니다. 참고로 행과 열의 위치가 동일한 경우 역대각선 빙고에 포함되며, 행 + 열 == 4인 경우는 대각선 빙고에 포함됩니다.
4.2 빙고 카운트
이제 rowCount\[\], columnCount\[\] 배열의 요소 중 하나가 5인 경우, diagnol, antidiagnol의 값이 5인 경우에 빙고하나가 체크됩니다.
빙고 카운트 함수를 만들어줍시다.
public class BingoBaord { private int MaxNumberCount = 25; private ArrayList<ArrayList<Integer>> bingoBoard; private int\[\] rowMap; private int\[\] columnMap; // 추가로 선언되는 변수들 private int\[\] rowCount; private int\[\] columnCount; private int diagonal; private int antidiagonal; BingoBaord(ArrayList<ArrayList<Integer>> bingoBoard){ this.bingoBoard = bingoBoard; rowMap = new int\[MaxNumberCount + 1\]; columnMap = new int\[MaxNumberCount + 1\]; } public void makeNumberMapping(){ for (int i = 0; i < 5; i){ for(int j = 0; j < 5; j){ int number= (int) bingoBoard.get(i).get(j); rowMap\[number\] = i; columnMap\[number\] = j; } } public void mark(int modertorNumber){ int rowPosition = rowMap\[modertorNumber\]; int colPosition = rowMap\[modertorNumber\]; rowCount\[rowPosition\]; columnCount\[colPosition\]; if (rowPosition == colPosition){ antidiagonal++; } if (rowPosition + colPosition == 4){ diagonal; } } public int getBingoCount(){ int totalBingoCount = 0; for (int count : rowCount){ if (count == 5) { totalBingoCount; } } for (int count : columnCount){ if(count == 5){ totalBingoCount; } } if (antidiagonal == 5){ totalBingoCount; } if (diagonal == 5){ totalBingoCount++; } return totalBingoCount; }} |
|---|
getBingoCount() 매서드는 빙고 카운트 수가 몇개인지를 정수로 반환합니다. 이를 Main()함수에서 조건문의 조건으로 사용할 수 있습니다.
5. Main()함수 완성
메인함수를 완성합니다. 앞서(3. 사회자입력 부분) 주석으로 처리해둔 부분을 코드로 작성하면 끝입니다.
public static void main(String\[\] args) { Scanner scanner = new Scanner(System.in); ArrayList<ArrayList<Integer>> board = input(scanner); System.out.println("board:" + board); //BingoBoard새로운 객체선언 후 매핑 BingoBaord bingoBaord = new BingoBaord(board); bingoBaord.makeNumberMapping(); for(int index = 1; index <26; index++ ) //사회자가 부르는 숫자 System.out.println("사회자 번호 입력: "); int moderatorNumber = scanner.nextInt(); //moderatorNumber 빙고판에서 체크 bingoBaord.mark(moderatorNumber); // 행, 열, 대각선, 역대각선 빙고를 체크한다. int bingoCount = bingoBaord.getBingoCount(); // 빙고갯수가 3개 이상인지 체크, ( >= 3이면, index출력, 종료) if (bingoCount >= 3){ System.out.println(index); break; } } } |
|---|
JAVA 클래스 설계에 대하여
클래스 설계를 중점적으로
이번 빙고문제 풀이를 하며 클래스 설계에 대한 상당히 많은 인사이트를 얻게되었습니다. 제가 얻은 인사이트를 여러분에게 공유하고자 합니다.
| 클래스의 이름에 걸맞게 기능을 만들어라. |
|---|
자바의 클래스는 이름에 걸맞는 기능들을 모은 메서드만 작성하도록 합니다. 데이터를 따로 관리해줘야 할 경우에는 데이터 클래스를 따로 만들 필요도 있겠습니다.
| 2. main() 함수에는 최대한 내부 동작을 숨겨라. |
|---|
메인함수에 노출되지 않아도 되는 기능들은 노출하지 않습니다. 예를들어 빙고판의 숫자를 체크하거나, 빙고의 갯수는 세는 일등은 메인함수에서 궂이 있지 않아도 됩니다.
마치 우리가 인형뽑기를 할 때 사용자는 인형을 움직이는 버튼들을 조작만할뿐, 집게가 어떻게 움직이고, 어떻게 인형 출구가 열리는지 등을 고려할 필요가 없는 것과 비슷합니다.
여러분들도 이점을 고려하여 클래스를 작성한다면 좋은 개발자가 될 수 있으리라 생각됩니다.



