![[인프런 워밍업 스터디 클럽] 0기 - 백엔드 과제 #5](https://cdn.inflearn.com/public/files/blogs/0f95464b-7983-4d65-adf0-2abbd550fa81/인프런-워밍업-클럽-0기.png)
[인프런 워밍업 스터디 클럽] 0기 - 백엔드 과제 #5
본 내용은 자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]를 듣고 작성한 글입니다.
5일차에는 클린코드에 대해서 배웠다. 클린한 코드가 왜 중요할까?
개발자들은 항상 요구사항을 구현할 뿐만 아니라 여러 명이서 협업을 하기 때문에 코드를 많이 읽는다. 이러한 과정에서 깔끔한 코드는 다른 사람을 위해서 혹은 나중에 다시 코드를 보고 판단해야 할 나를 위해서 필수적이다.
[과제에서 사용한 tool]
운영체제 : MacBook Air M1, 2020
Java : 17.0.9-amzn
IDE : IntelliJ Ultimate
과제
목표
주어진 코드를 클린하게 개선해보면서 클린코드에 대한 감을 익히기
[제시된 코드]
public class Main {
public static void main(String[] args) {
System.out.print("숫자를 입력하세요 : ");
Scanner scanner = new Scanner(System.in);
int a = scanner.nextInt();
int r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0;
for (int i = 0; i < a; i++) {
double b = Math.random() * 6;
if (b >= 0 && b < 1) {
r1++;
} else if (b >= 1 && b < 2) {
r2++;
} else if (b >= 2 && b < 3) {
r3++;
} else if (b >= 3 && b < 4) {
r4++;
} else if (b >= 4 && b < 5) {
r5++;
} else if (b >= 5 && b < 6) {
r6++;
}
}
System.out.printf("1번 눈금이 %d번 나왔습니다.\n", r1);
System.out.printf("2번 눈금이 %d번 나왔습니다.\n", r2);
System.out.printf("3번 눈금이 %d번 나왔습니다.\n", r3);
System.out.printf("4번 눈금이 %d번 나왔습니다.\n", r4);
System.out.printf("5번 눈금이 %d번 나왔습니다.\n", r5);
System.out.printf("6번 눈금이 %d번 나왔습니다.\n", r6);
}
}
[반복되는 로직 처리하기]
반복이 세 군데에서 일어나고 있다.
변수 r1~r6
for문
출력문
먼저, 변수 r1 ~ r6를 배열에 담아보자. 그러면서 동시에 변수명을 각각의 주사위 면이 나온 횟수
를 뜻하는 diceFaceCounts로 바꿔보자.
int[] diceFaceCounts = new int[6];
for (int diceFaceCount : diceFaceCounts) {
diceFaceCount = 0;
}
그 다음에 for문 안에 특정하게 반복되는 숫자들의 패턴이 보인다. 인덱싱을 해줘서 for문을 만들어보자.
for (int i = 0; i < a; i++) {
double b = Math.random() * 6;
for (int j = 0; j < diceFaceCounts.length; j++) {
if (b >= j && b < j + 1) {
diceFaceCounts[j]++;
}
}
}
이어서 출력문도 1~6까지의 숫자들이 같은 패턴으로 나열되어 있다. 인덱싱을 해 간단하게 나타내주자.
for (int i = 0; i < diceFaceCounts.length; i++) {
System.out.printf((i + 1) + "번 눈금이 %d번 나왔습니다.\n", diceFaceCounts[i]);
}
1차적으로 리팩토링한 코드는 다음과 같다.
public class Main1 {
public static void main(String[] args) {
System.out.print("숫자를 입력하세요 : ");
Scanner scanner = new Scanner(System.in);
int a = scanner.nextInt();
int[] diceFaceCounts = new int[6];
for (int diceFaceCount : diceFaceCounts) {
diceFaceCount = 0;
}
for (int i = 0; i < a; i++) {
double b = Math.random() * 6;
for (int j = 0; j < diceFaceCounts.length; j++) {
if (b >= j && b < j + 1) {
diceFaceCounts[j]++;
}
}
}
for (int i = 0; i < diceFaceCounts.length; i++) {
System.out.printf((i + 1) + "번 눈금이 %d번 나왔습니다.\n", diceFaceCounts[i]);
}
}
}
[메서드로 기능 분리하기]
다음은 이 코드들이 무엇을 하는 건지 명확하게 표현이 되어 있지 않다. 이러한 점을 보완하기 위해 메서드로 코드들을 나눠보자.
Scanner scanner = new Scanner(System.in);
int a = scanner.nextInt();
숫자를 입력받는 코드이다.
메서드명 : inputNumber()
반환타입 : int
파라미터 : X
public static int inputNumber() {
Scanner scanner = new Scanner(System.in);
return scanner.nextInt();
}
그 다음으로 주사위에 면을 생성하는 로직이다.
int[] diceFaceCounts = new int[6];
for (int diceFaceCount : diceFaceCounts) {
diceFaceCount = 0;
}
메서드명 : createDiceFaceCounts()
반환타입 : int[]
파라미터 : int faceNumbers
public static int[] createDiceFaceCounts(int faceNumbers) {
int[] diceFaceCounts = new int[faceNumbers];
for (int diceFaceCount : diceFaceCounts) {
diceFaceCount = 0;
}
return diceFaceCounts;
}
그 다음엔 주사위면이 각각 몇 회씩 나왔는지 알려주는 로직이다. 2중 for문이라 한 번에 읽히지가 않는다. 기능 단위로 덩어리 지어보자.
for (int i = 0; i < a; i++) {
double b = Math.random() * 6;
for (int j = 0; j < diceFaceCounts.length; j++) {
if (b >= j && b < j + 1) {
diceFaceCounts[j]++;
}
}
}
b >= j && b < j + 1 // 제일 안쪽의 조건문인데 뭘 의미하는건지 구분이 안간다.
메서드명 : isDiceNumber
반환타입 : boolean
파라미터 : int diceNumber, int i
public static boolean isDiceNumber(int diceNumber, int i) {
return diceNumber >= i && diceNumber < i + 1;
}
두번째로는 randomDiceNumber와 알맞는 주사위면의 횟수를 올려주는 로직이다.
메서드명 : increaseRollCounts
반환타입 : void
파라미터 : int randomDiceNumber, int[] diceFaceCounts
public static void increaseRollCounts(int randomDiceNumber, int[] diceFaceCounts) {
for (int j = 0; j < diceFaceCounts.length; j++) {
if (isDiceNumber(randomDiceNumber, j)) { // isDiceNumber메서드를 넣어줬다
diceFaceCounts[j]++;
}
}
}
마지막으로 printRollCounts 메서드를 만들어보자.
public static void printRollCounts(int[] diceFaceCounts) {
for (int i = 0; i < diceFaceCounts.length; i++) {
System.out.printf((i + 1) + "번 눈금이 %d번 나왔습니다.\n", diceFaceCounts[i]);
}
}
총 정리해보면 다음과 같다.
public class Main {
public static void main(String[] args) {
System.out.print("숫자를 입력하세요 : ");
int number = inputNumber();
int[] diceFaceCounts = createDiceFaceCounts(6);
for (int i = 0; i < number; i++) {
int randomDiceNumber = (int) (Math.random() * 6 + 1);
increaseRollCounts(randomDiceNumber, diceFaceCounts);
}
printRollCounts(diceFaceCounts);
}
public static int inputNumber() {
Scanner scanner = new Scanner(System.in);
return scanner.nextInt();
}
public static int[] createDiceFaceCounts(int faceNumbers) {
int[] diceFaceCounts = new int[faceNumbers];
for (int diceFaceCount : diceFaceCounts) {
diceFaceCount = 0;
}
return diceFaceCounts;
}
public static boolean isDiceNumber(int diceNumber, int i) {
return diceNumber >= i && diceNumber < i + 1;
}
public static void increaseRollCounts(int randomDiceNumber, int[] diceFaceCounts) {
for (int j = 0; j < diceFaceCounts.length; j++) {
if (isDiceNumber(randomDiceNumber, j)) {
diceFaceCounts[j]++;
}
}
}
public static void printRollCounts(int[] diceFaceCounts) {
for (int i = 0; i < diceFaceCounts.length; i++) {
System.out.printf((i + 1) + "번 눈금이 %d번 나왔습니다.\n", diceFaceCounts[i]);
}
}
}
하지만, 여전히 문제가 있다. 누가
이 메서들을 동작시키는 건지 명시가 안되어 있다. 이 문제를 class화 하여 해결할 수 있다. 이 때, 클래스는 의인화
를 하여 마치 메서드를 하는 행위라고 생각하면 클래스 나누기가 수월하다.
OutputView class
printNumberInput()
printRollCounts()
InputView class
inputNumber()
Dice class
isDiceNumber()
Player class
createDiceFaceCounts()
Game class
increaceRollCounts()
incrementFaceCount()
public class InputView {
public static int inputNumber() {
Scanner scanner = new Scanner(System.in);
return scanner.nextInt();
}
}
public class OutputView {
public static void printNumberInput() {
System.out.print("숫자를 입력하세요 : ");
}
public static void printRollCounts(int[] diceFaceCounts) {
for (int i = 0; i < diceFaceCounts.length; i++) {
System.out.printf((i + 1) + "번 눈금이 %d번 나왔습니다.\n", diceFaceCounts[i]);
}
}
}
public class Dice {
public static boolean isDiceNumber(int diceNumber, int i) {
return diceNumber >= i && diceNumber < i + 1;
}
}
public class Player {
public static int[] createDiceFaceCounts(int faceNumbers) {
int[] diceFaceCounts = new int[faceNumbers];
for (int diceFaceCount : diceFaceCounts) {
diceFaceCount = 0;
}
return diceFaceCounts;
}
}
public class Game {
public static void increaseRollCounts(int randomDiceNumber, int[] diceFaceCounts) {
for (int j = 0; j < diceFaceCounts.length; j++) {
if (Dice.isDiceNumber(randomDiceNumber, j)) {
diceFaceCounts[j]++;
}
}
}
public static void incrementFaceCount(int number, int[] diceFaceCounts) {
for (int i = 0; i < number; i++) {
int randomDiceNumber = (int) (Math.random() * 6 + 1);
Game.increaseRollCounts(randomDiceNumber, diceFaceCounts);
}
}
}
public class Main {
public static void main(String[] args) {
OutputView.printNumberInput();
int number = InputView.inputNumber();
int[] diceFaceCounts = Player.createDiceFaceCounts(6);
Game.incrementFaceCount(number, diceFaceCounts);
OutputView.printRollCounts(diceFaceCounts);
}
}
하.지.만 객체는 상태(멤버 변수)와 행동(메서드)를 가지고, 객체 지향 프로그래밍은 이러한 객체들이 서로 협력하는 프로그래밍을 말한다. 따라서, 상태들을 추가해주고 인스턴스화한 객체가 할 수 있는 행동에 대해서는 static을 제거해준다.
댓글을 작성해보세요.