1. 함수 추출하기
- 코드 조각을 찾아 무슨일을 하는지 파악한다음, 독립된 함수로 추출하고 목적에 맞는 이름을 붙인다.
- '목적과 구현을 분리'하는 방식이 가장 합리적인 기준으로 보인다.
- 코드를 보고 무슨 일을 하는지 파악하는 데 한참이 걸린다면 그 부분을 함수로 추출한뒤 '무슨 일'에 걸맞는 이름을 짓는다.
- 함수가 짧으면 캐싱하기가 더 쉽기 때문에 컴파일러가 최적화하는 데 유리할 때가 많다.
- 짧은 함수의 이점은 이름을 잘 지어야만 발휘되므로 이름 짓기에 특별히 신경 써야 한다.
절차
- 함수를 새로 만들고 목적을 잘 드러내는 이름을 붙힌다.
- 추출할 코드를 원본 함수에서 복사하여 새 함수에 붙혀 넣는다.
- 추출한 코드 중 원본 함수의 지역 변수를 참조하거나 추출한 함수의 유효범위를 벗어나는 변수는 없는지 검사한다. 있다면 매개변수로 전달한다.
- 변수를 다 처리했다면 컴파일한다.
- 원본 함수에서 추출한 코드 부분을 새로 만든 함수를 호출하는 문장으로 바꾼다.
- 테스트한다.
- 다른 코드에 방금 추출한 것과 똑같거나 비슷한 코드가 없는지 살핀다. 있다면 방금 추출한 새 함수를 호출하도록 바꿀지 검토한다.
2. 함수 인라인하기
- 목적이 분명히 드러나는 이름의 짤막한 함수를 이용하기 권한다. 그래야 코드가 명료해지고 이해하기 쉬워지기 때문이다.
절차
- 다형 메서드인지 확인한다.
- 인라인할 함수를 호출하는 곳을 모두 찾는다.
- 각 호출문을 함수 본문으로 교체한다.
- 하나씩 교체할 때마다 테스트한다.
- 함수 정의를 삭제한다.
3. 변수 추출하기
- 표현식이 너무 복잡해서 이해하기 어려울 때가 있다. 이럴 때 지역 변수를 활용하면 표현식을 쪼개 관리하기 더 쉽게 만들 수 있다.
- 그러면 복잡한 로직을 구성하는 단계마다 이름을 붙일 수 있어서 코드의 목적을 훨씬 명확하게 드러낼 수 있다.
- 변수 추출을 고려한다고 함은 표현식에 이름을 붙이고 싶다는 뜻이다.
- 덩치가 큰 클래스에서 공통 동작을 별도 이름으로 뽑아내서 추상화해두면 그 객체를 다룰 때 쉽게 활용할 수 있어서 매우 유용하다.
절차
- 추출하려는 표현식에 부작용은 없는지 확인한다.
- 불변 변수를 하나 선언하고 이름을 붙일 표현식의 복제본을 대입한다.
- 원본 표현식을 새로 만든 변수로 교체한다.
- 테스트한다.
- 표현식을 여러 곳에서 사용한다면 각가을 새로 만든 변수로 교체한다. 하나 교체할 때마다 테스트를 한다.
4. 변수 인라인하기
절차
- 대입문의 우변에서 부작용이 생기지 않는지 확인한다.
- 변수가 불변으로 선언되지 않았다면 불변으로 만든 후 테스트한다.
- 이 변수를 가장 처음 사용하는 코드를 찾아서 대입문 우변의 코드로 바꾼다.
- 테스트한다.
- 변수를 사용하는 부분을 모두 교체할 때까지 이 과정을 반복한다.
- 변수 선언문과 대입문을 지운다.
- 테스트한다.
5. 함수 선언 바꾸기
- 연결부를 잘 정의하면 시스템에 새로운 부분을 추가하기가 쉬워지는 반면, 잘못 정의하면 지속적인 방해 요인으로 작용하여 소프트웨어 동작을 파악하기 어려워지고 요구사항이 바뀔 때 적절히 수정하기 어렵게 한다.
- 연결부에서 가장 중요한 요소는 함수의 이름이다.
- 이름이 좋으면 함수의 구현 코드를 살펴볼 필요 없이 호출문만 보고도 무슨 일을 하는지 파악할 수 있다.
- 좋은 이름을 떠올리는 데 효과적인 방법이 하나있다. 바로 주석을 이용해 함수의 목적을 설명해 보는 것이다.
절차
- 매개변수를 제거하려거든 먼저 함수 본문에서 제거 대상 매개변수를 참조하는 곳은 없는지 확인한다.
- 메서드 선언을 원하는 형태로 바꾼다.
- 기존 메서드 선언을 참조하는 부분을 모두 찾아서 바뀐 형태로 수정한다.
- 테스트한다.
6. 변수 캡슐화하기
- 리팩터링은 결국 프로그램의 요소를 조작하는 일이다.
- 데이터는 함수보다 다루기가 까다로운데, 그 이유는 이런 식으로 처리할 수 없기 때문이다.
- 데이터는 참조하는 모든 부분을 한 번에 바꿔야 코드가 제대로 작동한다.
- 데이터는 유효범위가 넓어질수록 다루기가 어려워진다. 전역 데이터가 골칫거리인 이유도 바로 여기에 있다.
- 접근할 수 있는 범위가 넓은 데이터를 옮길 때는 먼저 그 데이터로의 접근을 독점하는 함수를 만드는 식으로 캡슐화하는 것이 가장 좋은 방법일 때가 많다.
- 데이터 재구성이라는 어려운 작업을 함수 재구성이라는 더 단순한 작업으로 변환되는 것이다.
- 데이터를 변경하고 사용하는 코드를 감시할 수 있는 확실한 통로가 되어주기 때문에 데이터 변경 전 검증이나 변경 후 추가 로직을 쉽게 끼워 넣을 수 있다.
- 데이터의 유효범위가 넓을수록 캡슐화해야 한다. 그래야 자주 사용하는 데이터에 대한 결합도가 높아지는 일을 막을 수 있다.
- 불변 데이터는 가변 데이터보다 캡슐화할 이유가 적다.
- 게다가 불변 데이터는 옮길 필요 없이 그냥 복제하면 된다.
- 데이터를 복제해 저장하여 나중에 원본이 변경돼서 발생하는 사고를 방지할 수 있다.
- 복제본 만들기가 번거로울 때가 많지만, 이런 복제가 서능에 주는 영향은 대체로 미미하다. 반면 원본을 그대로 사용하면 나중에 디버깅하기 어렵고 오래 걸릴 위험이 있다.
절차
- 변수로의 접근과 갱신을 전담하는 캡슐화 함수들을 만든다.
- 정적 검사를 수행한다.
- 변수를 직접 참조하던 부분을 모두 적절한 캡슐화 함수 호출로 바꾼다. 하나씩 바꿀 때마다 테스트를 한다.
- 변수의 접근 범위를 제한한다.
- 테스트한다.
- 변수 값이 레코드라면 레코드 캡슐화하기를 적용할지 고려해본다.
참고
리팩터링 2판