8. 기능 이동
아래에서는 요소를 다른 컨텍스트로 옮기는 일에 대해 이야기합니다.
- 함수 옮기기, 필드 옮기기
- 문장을 함수로 옮기기, 문장을 호출한 곳으로 옮기기, 문장 슬라이드하기, 인라인 코드를 함수 호출로 바꾸기
- 반복문 쪼개기, 반복문을 파이프라인으로 바꾸기
- 죽은 코드 제거하기
#
8.1 함수 옮기기#
배경.- 좋은 소프트웨어의 설계 핵심은 모듈화가 얼마나 잘 되어 있느냐를 뜻하는 모듈성입니다.
- 모듈성은 프로그램을 수정할 때 작은 일부만 이해하도록 해주는 것입니다.
- 모든 함수는 어떤 컨텍스트 안에서 존재하며, 객체 지향의 핵심 모듈화 컨텍스트는 클래스입니다.
#
절차- 선택한 함수가 현재 컨텍스트에서 사용 중인 프로그램 요소를 살펴보고, 옮길 필요를 확인합니다.
- 선택한 함수가 다형 메서드인지 확인합니다.
- 선택한 함수를 타깃 컨텍스트로 복사합니다.
- 정적 분석을 수행합니다.
- 소스 컨텍스트에서 타킷 함수를 참조할 방법을 찾아 반영합니다.
- 소스 함수를 타깃 함수의 위임 함수가 되도록 수정합니다.
- 테스트합니다.
- 소스 함수를 인라인할지 고민합니다.
#
예시#
중첩 함수를 최상위로 옮기기- 분석하기.
- 함수를 최상위로 복사하기
- 문제가 없으므로 calculateDistance 함수로 옮깁니다. (top_calculateDistance 도.)
- 수정합니다.
- 테스트 및 함수의 이름을 바꿔주고, 인라인 처리도 합니다.
#
다른 클래스로 옮기기다음의 순으로 리팩터링이 진행됩니다.
#
8.2 필드 옮기기#
배경- 프로그램의 힘은 데이터 구조에서 나옵니다.
- 데이터 구조는 매우 중요하나, 제대로 하기 어렵습니다.
- 데이터 구조가 적절치 않다고 판단하면 곧바로 수정합니다.
#
절차- 소스 필드가 캡슐화되어 있지 않다면 캡슐화합니다.
- 테스트합니다.
- 타깃 객체에 필드들을 생성합니다.
- 정적 검사를 수행합니다.
- 소스 객체에서 타깃 체를 참조할 수 있는지 확인합니다.
- 접근자들이 타깃 필드를 사용하도록 수정합니다.
- 테스트합니다.
- 소스 필드를 제거합니다.
- 테스트합니다.
#
예시#
기본 예시- 고객 클래스와 계약 클래스에서 시작합니다.
- 필드를 캡슐화합니다.
- 필드와 접근자를 추가합니다.
- 새로운 필드를 사용하도록 수정합니다.
#
공유 객체로 이동- 필드와 메서드 생성
- 테스트 후, 변경 후 기존 코드 삭제
#
8.3 문장을 함수로 옮기기#
배경- 중복 제거는 코드를 건강하게 관리하는 효과적인 방법 중 하나입니다.
- 문장들을 함수로 옮기려면 그 문장들이 피호출 함수의 일부라는 확신이 있어야합니다.
#
절차- 반복 코드가 함수 호출 부분과 멀리 떨어져 있다면 문장 슬라이드하기를 적용해 근처로 옮깁니다.
- 타깃 함수를 호출하는 곳이 한 곳뿐이면, 단순히 소스 위치에서 해당 코드를 잘라내어 피호출 함수로 복하고 테스트합니다.
- 호출자가 둘 이상이면 호출자 중 하나에서 타깃 함수 호출 부분과 그 함수로 옮기려는 문장들을 함께 다른 함수로 추출합니다. 추출한 함수에는 기억하기 쉬운 임시 이름을 지어줍니다.
- 다른 호출자 모두가 방금 추출한 함수를 사용하도록 수정합니다. 하나씩 수정할 때마다 테스트합니다.
- 모든 호출자가 새로운 함수를 사용하게 되면 원래 함수를 새로운 함수 안으로 인라인한 후 원래 함수를 제거합니다.
- 새로운 함수의 이름을 원래 함수의 이름으로 바꿔줍니다.
#
예시- 사진 관련 데이터를 HTML로 내보내는 코드를 준비합니다.
- 함수 추출합니다.
- 새로운 함수를 사용하도록 수정합니다.
- emitPhotoData() 함수를 인라인합니다.
- 함수 이름을 바꿔서 마무리 합니다.
#
8.4 문장을 호출한 곳으로 옮기기#
배경- 함수는 프로그래머가 쌓아 올리는 추상화의 기본 빌딩 블록
- 옮기기 리팩터링을 통해서 독립적으로 수정가능합니다.
#
절차- 호출자가 한두개뿐이고 피호출 함수도 간단한 단순한 상황이면, 피호출 함수의 처음 줄을 잘라내어 호출자들로 복사해서 넣습니다.
- 더 복잡한 경우에는 모든 문장을 함수로 추출한 다음 검색하기 쉬운 임시 이름을 지어줍니다.
- 원래 함수를 인라인합니다.
- 추출된 함수의 이름을 원래 함수의 이름으로 변경합니다.
#
예시- 3번 코드의 반대와 가까워서 생략함
#
8.5 인라인 코드를 함수 호출로 바꾸기#
배경- 함수는 여러 동작을 하나로 바꿔줍니다.
#
절차- 인라인 코드를 함수 호출로 변경합니다.
- 테스트합니다.
#
8.6 문장 슬라이드하기#
배경- 관련된 코드들이 가까이 모여 있다면 이해하기가 더 쉽습니다.
- 관련 코드를 모으는 것은 다른 리팩터링의 준비 단계로 행해집니다.
#
절차- 코드 조각을 이동할 목표 위치를 찾습니다. 이때 변경을 했을 때 문제가 있다고 판단하면 포기합니다.
- 코드 조각을 원래 위치에서 잘라내어 목표 위치에 붙여 넣습니다.
- 테스트합니다.
#
8.7 반복문 쪼개기#
배경- 종종 반복문 하나에서 두가지 일을 수행하면 반복문 수정시 두가지 일 모두를 이해하고 진행해야합니다.
- 다만 이는 최적화와 다르게 때문에 이러한 방식이 있습니다. 다만 이는 성능상 문제로 인해 필요하다고 판단되는 경우에 맞춰서 모아도 문제는 없습니다.
#
절차- 반복문을 복제해 두 개로 만듭니다.
- 반복문이 중복되어 생기는 부수효과를 파악해서 제거합니다.
- 테스트합니다.
- 완료되었으면 각 반복문을 함수로 추출할지 고민합니다.
#
예시- 리팩터링 전
- 리팩터링 후
- 알고리즘 리팩터링까지 젹용
#
8.8 반복문을 파이프라인으로 바꾸기#
배경- 파이프라인을 통해서 이해하기 쉬워집니다.
#
절차- 반복문에서 사용하는 컬렉션을 가리키는 변수를 하나 만듭니다.
- 반복문의 첫 줄부터 시작해서, 각각의 단위 행위를 적절한 컬렉션 파이프라인 연산으로 대체합니다.
- 반복문의 모든 동작을 대체했다면 기존의 반복문을 없앱니다.
#
예시- 인도에 자리한 사무시을 찾아, 도시명과 전화번호를 반환
- 리팩토링 후
#
8.9 죽은 코드 제거하기#
배경- 코드가 더 이상 사용되지 않는다면 지워야합니다.
- 최신 컴파일러는 사용하지 않는 코드를 표시해줍니다.
#
절차- 죽은 코드를 외부에서 참조할 수 있는 경우라면 혹시라도 호출하는 곳이 있는지 확인합니다.
- 없다면 죽은 코드를 제거합니다.
- 테스트합니다.