클린 코드의 목적은 가독성 향상, 유지보수성 향상, 버그 감소 등 소프트웨어의 품질을 높이는데 있다.
1. 의도가 명확하게 드러나는 이름 사용
변수, 함수, 클래스 등의 이름은 해당 역할이나 의도를 명확하게 나타낸다.
2. 작은 함수와 클래스 작성
함수와 클래스는 각각 하나의 역할을 수행하고 작고 응집력 있는 단위로 분리한다.
3. 읽기 쉽고 명확한 코드 작성
복잡한 로직이 필요한 경우에도 코드를 최대한 명확하고 읽기 쉽게 작성한다.
4. 주석을 최소화하고 명확한 주석 작성
이해하기 어려운 부분이 있다면 명확하고 간결한 주석을 추가한다.
5. 중복 제거
중복된 코드는 문제를 발생시킬 가능성이 높으므로 중복을 최소화한다.
6. 테스트 가능한 코드 작성
코드는 테스트하기 쉬워야 하며, 테스트 케이스 작성을 고려하여 설계한다.
아래의 코드 수정해보기
public static void main(String[] args) {
System.out.println("숫자를 입력하세요 : ");
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번 나왔습니다.", r1);
System.out.printf("2은 %d번 나왔습니다.", r2);
System.out.printf("3은 %d번 나왔습니다.", r3);
System.out.printf("4은 %d번 나왔습니다.", r4);
System.out.printf("5은 %d번 나왔습니다.", r5);
System.out.printf("6은 %d번 나왔습니다.", r6);
}
1. 변수명 변경
public static void main(String[] args) {
System.out.println("숫자를 입력하세요 : ");
Scanner scanner = new Scanner(System.in);
int rounds = scanner.nextInt();
int count1 = 0, count2 = 0, count3 = 0, count4 = 0, count5= 0, count6= 0;
for (int i = 0; i < rounds; i++) {
double rollResult = Math.random() * 6;
if (rollResult >= 0 && rollResult < 1) {
count1++;
} else if (rollResult >=1 && rollResult <2) {
count2++;
} else if (rollResult >=2 && rollResult <3) {
count3++;
} else if (rollResult >=3 && rollResult <4) {
count4++;
} else if (rollResult >=4 && rollResult <5) {
count5++;
} else if (rollResult >=5 && rollResult <6) {
count6++;
}
}
System.out.printf("1은 %d번 나왔습니다.", count1);
System.out.printf("2은 %d번 나왔습니다.", count2);
System.out.printf("3은 %d번 나왔습니다.", count3);
System.out.printf("4은 %d번 나왔습니다.", count4);
System.out.printf("5은 %d번 나왔습니다.", count5);
System.out.printf("6은 %d번 나왔습니다.", count6);
}
주사위 굴리는 횟수: a -> rounds
주사위 결과: b -> rollResult
숫자별 나온 횟수: r0 -> count0
2. 메서드 분리
static int count1 = 0, count2 = 0, count3 = 0, count4 = 0, count5= 0, count6= 0;
public static void main(String[] args) {
int rounds = getRounds();
rollTheDice(rounds);
printResult();
}
public static int getRounds() {
System.out.println("숫자를 입력하세요 : ");
Scanner scanner = new Scanner(System.in);
return scanner.nextInt();
}
public static void rollTheDice(int rounds) {
for (int i = 0; i < rounds; i++) {
double rollResult = Math.random() * 6;
if (rollResult >= 0 && rollResult < 1) {
count1++;
} else if (rollResult >=1 && rollResult <2) {
count2++;
} else if (rollResult >=2 && rollResult <3) {
count3++;
} else if (rollResult >=3 && rollResult <4) {
count4++;
} else if (rollResult >=4 && rollResult <5) {
count5++;
} else if (rollResult >=5 && rollResult <6) {
count6++;
}
}
}
public static void printResult() {
System.out.printf("1은 %d번 나왔습니다.", count1);
System.out.printf("2은 %d번 나왔습니다.", count2);
System.out.printf("3은 %d번 나왔습니다.", count3);
System.out.printf("4은 %d번 나왔습니다.", count4);
System.out.printf("5은 %d번 나왔습니다.", count5);
System.out.printf("6은 %d번 나왔습니다.", count6);
}
기능별로 메소드를 분리하여 각각의 메서드가 하나의 역할을 수행하도록 했다.
3. 중복 코드 제거
static int[] counts = new int[6];
public static void rollTheDice(int rounds) {
for (int i = 0; i < rounds; i++) {
double rollResult = Math.random() * 6;
counts[(int)rollResult]++;
}
}
public static void printResult() {
for (int i = 0; i < 6; i++) {
System.out.printf("%d은 %d번 나왔습니다.
", i+1, counts[i]);
}
}
반복문과 int 배열을 사용해서 중복되는 코드 제거했다.
4. 클래스 분리와 Magic Number 제거
public class GameApplication {
public static void main(String[] args) {
DiceGame diceGame = new DiceGame();
diceGame.gameStart();
}
}
메인 메소드가 아주 간결해졌다.
interface PlayOption<T> {
public int getRounds();
public T getItemType();
}
class DicePlayOption<T> implements PlayOption<T> {
@Override
public int getRounds() {
System.out.println("주사위 돌릴 횟수를 입력하세요 : ");
Scanner scanner = new Scanner(System.in);
return scanner.nextInt();
}
@Override
public T getItemType() {
System.out.println("주사위의 최대값를 입력하세요 : ");
Scanner scanner = new Scanner(System.in);
return (T) (Integer) scanner.nextInt();
}
}
게임 세팅이 달라졌을 때 확장성을 고려해서 PlayOption 인터페이스와 상속받는 DicePlayOption 클래스를 만들었다. 주사위 면의 수도 입력받아 다양한 주사위를 사용할 수 있다.
아이템 타입을 정의하는 메소드는 다른 게임에서 사용했을 때를 고려해서 제네릭을 사용했다.
class DiceGame {
private int[] counts;
private PlayOption playOption = new DicePlayOption();
public void gameStart() {
int round = playOption.getRounds();
int diceType = playOption.getItemType();
counts = new int[diceType];
rollTheDice(round, diceType);
printResult(diceType);
}
public void rollTheDice(int round, int diceType) {
for (int i = 0; i < round; i++) {
double rollResult = Math.random() * diceType;
counts[(int)rollResult]++;
}
}
public void printResult(int diceType) {
for (int i = 0; i < diceType; i++) {
System.out.printf("%d은 %d번 나왔습니다.\n", i+1, counts[i]);
}
}
}