1. 리팩토링
1) 매직 넘버를 기호 상수로 치환
- 매직 넘버 : 소스 코드안 특정한 숫자를 직접 적은 것
- 기호 상수 : 숫자에 상징이 되는 기호를 선언한 것
① 이유
> 매직 넘버의 의미를 알기 어려움
> 매직 넘버는 수정하기 어려움
② 리팩토링 카탈로그
이름 | 매직 넘버를 기호 상수로 치환 |
상황 | 상수를 사용함 |
문제 | - 매직 넘버는 의미를 알기 어려움 - 매직 넘버가 여러 곳에 있으면 변경하기 어려움 |
해법 | 매적 넘버를 기호 상수로치환함 |
결과 | o 상수의 의미를 알기 쉬워짐 o 기호 상수의 값을 변경하면 상수를 사용하는 모든 곳이 변경됨 x 이해하기 어려운 이름을 사용하면 오해가 생길 수 있음 |
방법 | 1. 기호 상수 선언하고 치환 후 컴파일 2. 테스트 |
2. 예제 프로그램
클래스명 | 역할 |
Robot | 로봇 클래스 |
Main | 로봇을 조작하는 클래스 |
1) 리팩토링 전
//Robot 클래스
public class Robot {
private final String _name;
public Robot(String name){
_name = name;
}
public void order(int command){
if(command == 0) {
System.out.println(_name + " walks.");
} else if(command == 1) {
System.out.println(_name + " stops.");
} else if(command == 2){
System.out.println(_name+" jumps.");
} else {
System.out.println("Command error. command = "+command);
}
}
}
- 0, 1, 2 라는 숫자는 로봇이 수행하는 명령어를 뜻하지만 그 부분만 보고선 의미를 알 순 없다.
//Main 클래스
public class Main {
public static void main(String[] args) {
Robot robot = new Robot("Andrew");
robot.order(0);// walk
robot.order(1);// stop
robot.order(2);// jump
}
}
- 주석을 달아 의미를 표기했지만, 주석보다는 기호 상수를 사용하는 것이 더 좋다.
2) 리팩토링 실행
ⓐ 기호 상수 선언
- public static final 클래스 필드 사용하기
- enum 사용하기
- public : 클래스 외부에서 참조할 수 있음
- static : 객체에 소속된 것이 아닌 클래스에 소속된 필드(모든 객체가 공유할 수있는 필드가 됨)
- final : 상수로 선언 => 잘못해서 할당하지 않도록 함
public static final int COMMAND_WALK = 0;
public static final int COMMAND_STOP = 1;
public static final int COMMAND_JUMP = 2;
ⓑ 매직 넘버를 기호 상수로 치환
public void order(int command){
if(command == COMMAND_WALK) {
System.out.println(_name + " walks.");
} else if(command == COMMAND_STOP) {
System.out.println(_name + " stops.");
} else if(command == COMMAND_JUMP){
System.out.println(_name+" jumps.");
} else {
System.out.println("Command error. command = "+command);
}
}
public static void main(String[] args) {
Robot robot = new Robot("Andrew");
robot.order(COMMAND_WALK);
robot.order(COMMAND_STOP);
robot.order(COMMAND_JUMP);
}
ⓒ 기호 상수에 의존하는 다른 매직 넘버를 찾아서 기호 상수를 사용한 표현식으로 변환
ⓓ 컴파일
ⓔ 테스트 : 모든 상수 치환이 끝나면 컴파일 해서 테스트/ 기호 상숫값을 변경한 후 테스트
3) 리팩토링 후
public class Main {
public static void main(String[] args) {
Robot robot = new Robot("Andrew");
robot.order(Robot.COMMAND_WALK);
robot.order(Robot.COMMAND_STOP);
robot.order(Robot.COMMAND_JUMP);
}
}
public class Robot {
public static final int COMMAND_WALK = 0;
public static final int COMMAND_STOP = 1;
public static final int COMMAND_JUMP = 2;
private final String _name;
public Robot(String name){
_name = name;
}
public void order(int command){
if(command == COMMAND_WALK) {
System.out.println(_name + " walks.");
} else if(command == COMMAND_STOP) {
System.out.println(_name + " stops.");
} else if(command == COMMAND_JUMP){
System.out.println(_name+" jumps.");
} else {
System.out.println("Command error. command = "+command);
}
}
}
3. 한 걸음 더 나아가기
1) 분류 코드를 클래스로 치환하기
- 분류 코드 : 사물의 종류를 표현하는 값
- 앞서 리팩토링은 다른 매직넘버를 넣어도 경고를 출력하지 않는다는 문제점이 있다.
ex) robot.order(0)
- 분류코드에 정수를 쓰지 않고 새 타입을 만들면 문제 해결!
// RobotCommand 클래스 새로 생성
public class RobotCommand {
private final String _name;
public RobotCommand(String name){
_name = name;
}
public String toString(){
return "[ RobotCommand: "+_name+" ]";
}
}
public class Robot{
private final String _name;
public static final RobotCommand COMMAND_WALK = new RobotCommand("WALK");
public static final RobotCommand COMMAND_STOP = new RobotCommand("STOP");
public static final RobotCommand COMMAND_JUMP = new RobotCommand("JUMP");
public Robot(String name){
_name=name;
}
public void order(RobotCommand command){
if(command ==COMMAND_WALK){
System.out.println(_name+" walks.");
} else if(command==COMMAND_STOP){
System.out.println(_name+" stops.");
} else if(command==COMMAND_JUMP){
System.out.println(_name+" jumps.");
} else {
System.out.println("Command error. command = "+command);
}
}
}
main은 그대로임
2) enum 이용
- 자바 5부터 enum으로 기호 상수를 표현할 수 있음
public class Robot {
private final String _name;
public enum COMMAND{
WALK(0),STOP(1),JUMP(2);
};
public Robot(String name){
_name = name;
}
public void order(COMMAND command){
if(command == COMMAND.WALK)
System.out.println(_name + " walks.");
else if(command == COMMAND.STOP)
System.out.println(_name + " stops.");
else if(command == COMMAND.JUMP)
System.out.println(_name+" jumps.");
else
System.out.println("Command error. command = "+command);
}
}
public class Main {
public static void main(String[] args) {
Robot robot = new Robot("Andrew");
robot.order(Robot.COMMAND.WALK);
robot.order(Robot.COMMAND.STOP);
robot.order(Robot.COMMAND.JUMP);
}
}
3) 기호 상수가 적합하지 않은 경우
- 배열 길이나 인덱스를 나타나는 데 기호 상수는 적합하지 않음
4) 바이트 코드에 내장된 상수에 주의하기
- 필드 값을 변경한 후 리컴파일하면 필드 값 자체는 변하지만, 그 값을 사용하는 다른 클래스에도 새로운 값이 전달될지는 알 수 없다. 그 클래스도 리컴파일 했을 때 새로운 값이 넘어감
예를들어 위 예제 프로그램에서
Robot 클래스의 필드값 walk, stop, jump 의 값을 변경하고 main을 리컴파일 하지 않으면 정상적으로 돌아가지 않음..
'Major > Java' 카테고리의 다른 글
[자바로 배우는 리팩토링 입문] 3장 어서션 도입 (0) | 2022.04.15 |
---|---|
[자바로 배우는 리팩토링 입문] 2장 제어 플래그 삭제 (0) | 2022.04.13 |
[자바로 배우는 리팩토링 입문] 0장 리팩토링이란 (0) | 2022.04.13 |
[Power Java Compact] 7장 연습문제 (0) | 2022.01.05 |
[Power Java Compact] 6장 연습문제 (0) | 2021.12.29 |