유키 히로시의 자바로 배우는 리팩토링 입문을 바탕으로 작성했습니다.
외부에서 보는 프로그램 동작은 바꾸지 않고 내부의 구조를 보기 좋게 개선하는 것
구조를 개선시키는 목적
리팩토링은 내부의 구조를 개선하는데
이러한 개선이 필요한 부분을 악취
가 난다고 표현한다.
'악취'란 표현은 마틴 파울러의 리팩토링책에서 나오는 말이다.
마틴파울러의 책에서 소개하는 악취의 종류는 다음과 같이 22가지지이다.
다 외우기 어려우니 그 중 특정할 키워드를 소개한다.
중복
여러 곳에 비슷한 코드가 흩어져있는 경우
겹치는 코드가 있을 경우 합칠 수 있는걸 찾아서 메서드 추출
or 클래스 추출
을 고려한다.
null 확인이 많다면 null 객체 도입
, 에러 확인이 많다면 에러 코드를 예외로 치환
을 고려해본다.
너무 긴 코드
메서드가 너무 길 경우 메서드 추출
클래스의 책임이 너무 과할 경우 클래스 추출
등을 고려해본다.
너무 많은
클래스 추출
이 지나치면 클래스가 너무 많아질 수 있다
클래스의 책임 범위가 너무 작을 경우 개수가 많아져 이해가 어렵기 때문에
중개자 제거
클래스 인라인화
메서드 인라인화
등을 고려
적절치 못한 이름
변수명, 필드명, 메서드명, 클래스명, 패키지명 등등
표현하고 싶은 개념과 이름이 일치하지 않을 경우 적절한 이름 부여
이름과 관련된 리팩토링
메서드 추출
메서드명 변경
설명용 변수 도입
임시 변수 분리
너무 공개적
메서드나 필드가 public으로 지정할 경우 외부에서 호출할 수도 있기 때문에 프로그램을 수정할 때 이를 수정하기 까다로움
필드의 경우 필드 캡슐화
를 통해 필드를 숨길 수 있다
클래스명도 과도한 공개를 할 경우 나중에 클래스명을 변경할 때 인스턴스를 생성하는 모든 곳에서 이름을 변경해야 하기에 생성자를 팩토리 메서드로 치환
을 검토한다.
객체 지향답지 않음
switch나 if문으로 동작을 분기하거나
잦은 instanceof의 사용하거나
int와 같은 기본 자료형에 집착하는 경우를
다형성 사용 및 클래스 자료형의 사용을 검토한다.
구체적인 리팩토링을 실시하기 전 유의해야 할 점
리팩토링해도 외부에서 보는 동작은 변하지 않아야하기에
적절한 테스트를 통해서 확인해야한다.
리팩토링 전후로 테스트를 실행하여 이전과 동작이 일치하는지 테스트
메서드를 리팩토링하는경우 이를 불러오는 클래스 단위에서 테스트를 진행
자바에서 많이 쓰이는 유닛 테스트용 프레임워크에는 JUnit
이 있다.
리팩토링을 진행할 때 필요한 부분을 한 번에 하나씩 진행해야한다.
여러 수정을 한꺼번에 처리할 경우 실수할 가능성이 올라가고 되돌리는 작업이 어려워진다.
또한 테스트를 진행할 때 하나씩 진행해야 단계별로 진행하기 용이해진다.
객체 간의 관계를 표현할 때 주로 UML, Unified Modeling Language를 사용한다.
이후 진행 될 리팩토링 방법에서 고쳐야 할 부분의 시스템을 설명할 때 등장할 수 있다.
Class Diagram은 클래스간의 관계를 표시한다.
interface Executable {
abstract void execute();
}
abstract class ParentClass {
Something _field1;
static int FIELD2;
abstract void method1();
void method2() {
...
}
static void method3() {
...
}
}
class ChildClass extends ParentClass implements Executable {
void method1() {
...
}
void execute() {
...
}
}
class Somthing {
int _value;
...
}
다음의 코드를 클래스 다이어그램으로 표현할 경우
정리
세 영역 중 위엔 클래스 or 인터페이스 명
중간 영역은 필드 부분
마지막 영역은 메서드 부분
화살표의 방향은 해당 클래스가 의존하는 클래스를 향하도록 한다.
----▷ 인터페이스 구현
──▷ 클래스 상속
◇──> 집합, 객체를 포함하는 쪽에서 포함되는 쪽으로
Sequence Diagram은 프로그램 동작 순서를 표현한다.
class Client {
Server _server = new Server();
...
void work() {
_server.open();
_server.print("Hello");
_server.close();
}
}
class Server {
Device device;
void open() {
_device = new Device();
...
}
void print(String s) {
_device.write(s);
}
void close() {
...
}
}
class Device {
void write(String s) {
...
}
}
다음의 코드를 클래스 다이어그램으로 표현할 경우
그림판 작업으로 인해 퀄리티는 감안하고 보기를 바랍니다..