리펙토링 2판 읽고 요약해보기 (~ end)
‘리펙토링’ 책을 읽고 마지막까지 요약해보려고 합니다.
기본적인 리펙토링
함수 추출하기
- 배경
- 코드 조각을 찾아 무슨 일이 하는지 파악
- 독립된 함수로 추출하고 목적에 맞는 이름 부여
- 함수 안에 들어갈 코드가 대여섯 줄을 넘어갈 때부터 슬슬 악취
- 이름짓기 신경쓰자
- 절차
- 함수를 새로 만들고 목적을 잘 드러내는 이름 부여
- 추출할 코드를 원본 함수에서 복사하여 새 함수 반영
- 추출한 코드 중 원본 함수의 지역 변수를 참조하거나 추출한 함수의 유효범위를 벗어나는 변수는 없는지 검사
- 컴파일 진행
- 원본 함수에서 추출한 코드 부분을 새로 만든 함수를 호출하는 문장으로 변경
- 테스트
- 만든 코드에 방금 추출한 것과 똑같거나 비슷한 코드가 없는지 확인
변수 추출하기
- 배경
- 표현식이 너무 복잡해서 이해하기 어려울 때
- 지역 변수를 활용하여 표현식을 쪼개 관리 용이해지고 싶을 때
- 변수 추출을 고려한다고 함은 표현식에 이름을 붙이고 싶다는 뜻
- 절차
- 추출하려는 표현식에 부작용은 없는지 확인
- 불변 변수를 하나 선언하고 이름을 붙일 표현식의 복제본 대입
- 새로 만든 변수 교체
- 테스트
- 표현식을 여러 곳에서 사용한다면 각각 새로 만든 변수 교체
변수 인라인하기
- 배경
- 변수 이름이 원래 표현식과 다를 바 없을 때
- 절차
- 대입문의 우측 표현식에서 부작용이 생기지 않는지 확인
- 변수가 불변으로 선언되지 않았다면 불변으로 변경 후 테스트
- 이 변수를 처음 사용하는 코드를 찾아서 대입문 우측 표현식 코드 변경
- 테스트
- 변수 선언문과 대입문 삭제
- 테스트
함수 선언 바꾸기
- 배경
- 이름이 좋으면 함수의 구현 코드를 살펴볼 필요 없이 호출문만 보고 파악 가능
- 활용 범위가 넓어질 뿐만 아니라 다른 모듈과의 결합 제거 가능
- 정답이 없다
- 절차
- 먼저 함수 본문에서 제거 대상 매개변수를 참조하는 곳이 있는지 확인
- 메소드 선언을 원하는 형태로 변경
- 기존 메소드 선언을 참조하는 부분을 모두 찾아서 바뀐 형태로 수정
- 테스트
변수 캡슐화하기
- 배경
- 데이터의 접근을 독점하는 함수로 캡슐화하는 것이 좋은 방법
- 객체 지행에서 객체의 데이터를 private 하게 유지해야 한다고 강조하는 이유
- 절차
- 변수 접근과 갱신을 전담하는 캡슐화 함수 작성
- 정적 검사 수행
- 변수를 직접 참조하던 부분을 모두 캡슐화 함수 호출
- 변수의 접근 범위 제한
- 테스트
변수 이름 바꾸기
- 배경
- 이름짓기
- 명확한 프로그래밍의 핵심
- 함수 호출 한 번으로 끝나지 않고 값이 영속되는 필드라면 이름 중요
- 이름짓기
- 절차
- 이름 바꿀 변수를 참조하는 곳은 모두 찾아서 하나씩 변경
- 테스트
매개변수 객체 만들기
- 배경
- 데이터 뭉치를 데이터 구조로 묶으면 데이터 사이의 관계 명확
- 데이터 구조에 담길 데이터를 공통으로 적용되는 동작을 추출해서 함수 작성
- 절차
- 적당한 데이터 구조가 아직 마련되지 않았다면 새로 작성
- 테스트
- 함수 선언 바꾸기에서 새로운 데이터 구조를 매개변수로 추가
- 테스트
- 함수 호출 시 새로운 데이터 구조 인스턴스를 넘기도록 수정
- 기존 매개변수로 사용하던 코드를 새로운 데이터 구조의 원소 사용
- 기존 매개변수 제거하고 테스트
여러 함수를 클래스로 묶기
- 배경
- 클래스로 묶을 때 장점
- 클라이언트의 객체의 핵심 데이터 변경 가능
- 파생 객체 일관적으로 관리
- 클래스로 묶을 때 장점
- 절차
- 함수들이 공유하는 공통 데이터 레코드 캡슐화
- 공통 레코드 사용하는 함수 각각 새 클래스 이동
- 데이터 조작하는 로직은 함수로 추출해서 새 클래스 이동
여러 함수를 변환 함수로 묶기
- 배경
- 변환함수
- 원본 데이터를 입력받아서 필요한 정보를 모두 도출한 후 각자 출력 데이터 필드에 넣어 반환
- 도출 로직이 중복되는 것 회피
- 변환함수
- 절차
- 변환할 레코드를 입력받아서 값을 그대로 반환하는 변환 함수 생성
- 묶을 함수 중 골라서 본문 코드를 변환 함수로 옮기고, 처리 결과를 레코드에 새로운 필드로 기록
- 클라이언트 코드가 이 필드를 사용하도록 수정
- 테스트
단계 쪼개기
- 배경
- 두 단계로 분리
- 코드를 별도 모듈로 분리하면, 그 차이를 코드에서 명확하게 확인 가능한 상태
- 절차
- 두 단계에 해당하는 코드를 독립 함수로 추출
- 테스트
- 중간 데이터 구조 생성해서 추출한 함수의 인수 추가
- 테스트
- 추출한 함수의 매새변수를 하나씩 검토하고 첫 단계에 사용한 것은 중간 데이터 구조로 이동
- 첫 단계 코드를 함수 추출하면서 중간 데이터를 반환하도록 변경
캡슐화
- 시스템에서 각 모듈이 자신을 제외한 다른 부분에 드러나지 않고 얼마나 잘 숨기는지
- 모듈을 분리하는 가장 중요한 기준
-
레코드, 컬랙션 캡슐화
- 기본형 객체 변경
- 기본형 데이터도 캡슐화 가능
- 임시 변수 질의 함수 변경
- 임시 변수가 리펙토링의 걸림돌일 때 적용
- 클래스 추출, 클레스 인라인
- 추출과 인라인의 클래스 버전
- 위임 숨기기
- 클래스 사이의 연결 관계를 숨길 때 유용
- 중개자 제거로 비대해지는 인터페이스 제한
- 알고리즘 교체
- 추출한 함수로 알고리즘을 모으는 시기가 오면 교체
기능 이동
- 다른 컨텍스트로 옮기는 일
- 리텍토링의 중요한 축
- 함수, 필드, 문장 함수 이동
- 함수도 문장 단위로 구성
-
문장 호출된 곳 이동
- 인라인 코드 함수 호출 변경
- 한 덩어리 문장이 기존 함수와 같은 일을 할 때 시도
- 문장 슬라이드
- 같은 함수 안에서 옮길 때 시도
- 반복문 분리
- 각자 반복문이 단 하나의 일만 수행하도록 보장
- 반복문 파이프라인 변경
- 반복문을 완전히 삭제
- 죽은 코드 제거
- 필요 없는 문장을 디지털 화염방사기로 삭제
데이터 조직화
- 변수 분리
- 하나의 값이 여러 목적으로 사용되면 혼란과 버그 발견
- 필드 이름 변경
- 변수 이름을 잘 짓는 일은 까다로우면서 중요
- 파생 변수 질의 함수 변경
- 변수 자체 제거
- 참조 값 변경
- 참조인지 값인지 햇갈릴 때 진행
- 매직 리터럴 변경
- 코드의 의미를 알기 어려운 리터럴이 보이면 변경
조건부 로직 간소화
- 조건문 분해, 통합
- 복잡한 조건문을 분해하고 명확하게 다듬고자 통합
- 중첩 조건문 보호 구문 변경
- 함수의 핵심로직 들어가기 전 무언가를 검사할 때 시도
- 조건부 로직 다형성 변경
- 똑같은 분기 로직이 여러 곳 등장할 때 시도
- 특이 케이스 추가
- Null 특이 케이스 처리가 거의 동일할 때 시도
- 어서션 추가
- 프로그램 상태 확인하고 결과에 따라 다르게 동작해야 할 때 시도
- 제어 플래그 탈출문 변경
- 제어 플래그로 동작하는 코드를 더 간소화할 때 시도
API 리펙토링
- 좋은 API
- 데이터를 갱신하는 함수와 조회만 하는 함수 명확히 구분할 수 있는 구조
- 질의 함수와 변경 함수 분리
- 데이터 갱신 함수와 조회 함수가 섞여 있을 때 시도
- 함수 매개변수화
- 값 하나 때문에 여러 개로 나뉜 함수를 합칠 때 시도
- 플래그 인수 제거
- 매개변수가 함수의 동작 모드만 바꾸면 제거
- 객체 통째로 이동
- 데이터 구조가 함수 사이를 넘어가서 많이 흩어질 때 시도
-
매개변수 질의 함수 변경
- 세터 제거
- 객체가 불변이길 원하면 적용
- 생성자 팩터리 함수 변경
- 호출자에 새로운 객체를 만들어 반환하려 할 때 시도
- 함수 명령 변경
- 함수를 객체로 변환하면서 추출하기 수월
- 수정된 값 반환
- 함수 안에서 데이터가 수정되었음을 알리기 용이
- 오류 코드 예외 변경
- 오류 코드에 의존하는 방식을 바꿀 때 시도
- 예외 사전확인 변경
- 문제가 되는 조건을 함수 호출 전에 검사할 수 있을 때 시도
상속 다루기
- 메소드, 필드, 생성자 본문 올리기 & 메소드, 필드 내리기
- 특정 기능을 상속 계층구조의 위나 아래로 옮길 때 시도
- 타입 코드 서브클래스 변경
- 필드 값에 따라 동작이 달라져서 서브클래스로 대체하고 싶을 때 시도
- 서브클래스 제거, 슈퍼클래스 추출
- 계층 사이 클래스를 추가&제거하는 리펙토링
-
계층 병합
- 서브클래스, 슈퍼클래스 위임 변경
- 잘 못된 곳에서 사용하거나 환경이 변한 경우에 상속을 위임으로 변경
Written on November 9, 2022