리펙토링 2판 읽고 요약해보기 (~ end)

‘리펙토링’ 책을 읽고 마지막까지 요약해보려고 합니다.

기본적인 리펙토링

함수 추출하기

  • 배경
    • 코드 조각을 찾아 무슨 일이 하는지 파악
    • 독립된 함수로 추출하고 목적에 맞는 이름 부여
    • 함수 안에 들어갈 코드가 대여섯 줄을 넘어갈 때부터 슬슬 악취
    • 이름짓기 신경쓰자
  • 절차
    • 함수를 새로 만들고 목적을 잘 드러내는 이름 부여
    • 추출할 코드를 원본 함수에서 복사하여 새 함수 반영
    • 추출한 코드 중 원본 함수의 지역 변수를 참조하거나 추출한 함수의 유효범위를 벗어나는 변수는 없는지 검사
    • 컴파일 진행
    • 원본 함수에서 추출한 코드 부분을 새로 만든 함수를 호출하는 문장으로 변경
    • 테스트
    • 만든 코드에 방금 추출한 것과 똑같거나 비슷한 코드가 없는지 확인

변수 추출하기

  • 배경
    • 표현식이 너무 복잡해서 이해하기 어려울 때
    • 지역 변수를 활용하여 표현식을 쪼개 관리 용이해지고 싶을 때
    • 변수 추출을 고려한다고 함은 표현식에 이름을 붙이고 싶다는 뜻
  • 절차
    • 추출하려는 표현식에 부작용은 없는지 확인
    • 불변 변수를 하나 선언하고 이름을 붙일 표현식의 복제본 대입
    • 새로 만든 변수 교체
    • 테스트
    • 표현식을 여러 곳에서 사용한다면 각각 새로 만든 변수 교체

변수 인라인하기

  • 배경
    • 변수 이름이 원래 표현식과 다를 바 없을 때
  • 절차
    • 대입문의 우측 표현식에서 부작용이 생기지 않는지 확인
    • 변수가 불변으로 선언되지 않았다면 불변으로 변경 후 테스트
    • 이 변수를 처음 사용하는 코드를 찾아서 대입문 우측 표현식 코드 변경
    • 테스트
    • 변수 선언문과 대입문 삭제
    • 테스트

함수 선언 바꾸기

  • 배경
    • 이름이 좋으면 함수의 구현 코드를 살펴볼 필요 없이 호출문만 보고 파악 가능
    • 활용 범위가 넓어질 뿐만 아니라 다른 모듈과의 결합 제거 가능
    • 정답이 없다
  • 절차
    • 먼저 함수 본문에서 제거 대상 매개변수를 참조하는 곳이 있는지 확인
    • 메소드 선언을 원하는 형태로 변경
    • 기존 메소드 선언을 참조하는 부분을 모두 찾아서 바뀐 형태로 수정
    • 테스트

변수 캡슐화하기

  • 배경
    • 데이터의 접근을 독점하는 함수로 캡슐화하는 것이 좋은 방법
    • 객체 지행에서 객체의 데이터를 private 하게 유지해야 한다고 강조하는 이유
  • 절차
    • 변수 접근과 갱신을 전담하는 캡슐화 함수 작성
    • 정적 검사 수행
    • 변수를 직접 참조하던 부분을 모두 캡슐화 함수 호출
    • 변수의 접근 범위 제한
    • 테스트

변수 이름 바꾸기

  • 배경
    • 이름짓기
      • 명확한 프로그래밍의 핵심
    • 함수 호출 한 번으로 끝나지 않고 값이 영속되는 필드라면 이름 중요
  • 절차
    • 이름 바꿀 변수를 참조하는 곳은 모두 찾아서 하나씩 변경
    • 테스트

매개변수 객체 만들기

  • 배경
    • 데이터 뭉치를 데이터 구조로 묶으면 데이터 사이의 관계 명확
    • 데이터 구조에 담길 데이터를 공통으로 적용되는 동작을 추출해서 함수 작성
  • 절차
    • 적당한 데이터 구조가 아직 마련되지 않았다면 새로 작성
    • 테스트
    • 함수 선언 바꾸기에서 새로운 데이터 구조를 매개변수로 추가
    • 테스트
    • 함수 호출 시 새로운 데이터 구조 인스턴스를 넘기도록 수정
    • 기존 매개변수로 사용하던 코드를 새로운 데이터 구조의 원소 사용
    • 기존 매개변수 제거하고 테스트

여러 함수를 클래스로 묶기

  • 배경
    • 클래스로 묶을 때 장점
      • 클라이언트의 객체의 핵심 데이터 변경 가능
      • 파생 객체 일관적으로 관리
  • 절차
    • 함수들이 공유하는 공통 데이터 레코드 캡슐화
    • 공통 레코드 사용하는 함수 각각 새 클래스 이동
    • 데이터 조작하는 로직은 함수로 추출해서 새 클래스 이동

여러 함수를 변환 함수로 묶기

  • 배경
    • 변환함수
      • 원본 데이터를 입력받아서 필요한 정보를 모두 도출한 후 각자 출력 데이터 필드에 넣어 반환
    • 도출 로직이 중복되는 것 회피
  • 절차
    • 변환할 레코드를 입력받아서 값을 그대로 반환하는 변환 함수 생성
    • 묶을 함수 중 골라서 본문 코드를 변환 함수로 옮기고, 처리 결과를 레코드에 새로운 필드로 기록
    • 클라이언트 코드가 이 필드를 사용하도록 수정
    • 테스트

단계 쪼개기

  • 배경
    • 두 단계로 분리
    • 코드를 별도 모듈로 분리하면, 그 차이를 코드에서 명확하게 확인 가능한 상태
  • 절차
    • 두 단계에 해당하는 코드를 독립 함수로 추출
    • 테스트
    • 중간 데이터 구조 생성해서 추출한 함수의 인수 추가
    • 테스트
    • 추출한 함수의 매새변수를 하나씩 검토하고 첫 단계에 사용한 것은 중간 데이터 구조로 이동
    • 첫 단계 코드를 함수 추출하면서 중간 데이터를 반환하도록 변경

캡슐화

  • 시스템에서 각 모듈이 자신을 제외한 다른 부분에 드러나지 않고 얼마나 잘 숨기는지
    • 모듈을 분리하는 가장 중요한 기준
  • 레코드, 컬랙션 캡슐화

  • 기본형 객체 변경
    • 기본형 데이터도 캡슐화 가능
  • 임시 변수 질의 함수 변경
    • 임시 변수가 리펙토링의 걸림돌일 때 적용
  • 클래스 추출, 클레스 인라인
    • 추출과 인라인의 클래스 버전
  • 위임 숨기기
    • 클래스 사이의 연결 관계를 숨길 때 유용
    • 중개자 제거로 비대해지는 인터페이스 제한
  • 알고리즘 교체
    • 추출한 함수로 알고리즘을 모으는 시기가 오면 교체

기능 이동

  • 다른 컨텍스트로 옮기는 일
    • 리텍토링의 중요한 축
  • 함수, 필드, 문장 함수 이동
    • 함수도 문장 단위로 구성
  • 문장 호출된 곳 이동

  • 인라인 코드 함수 호출 변경
    • 한 덩어리 문장이 기존 함수와 같은 일을 할 때 시도
  • 문장 슬라이드
    • 같은 함수 안에서 옮길 때 시도
  • 반복문 분리
    • 각자 반복문이 단 하나의 일만 수행하도록 보장
  • 반복문 파이프라인 변경
    • 반복문을 완전히 삭제
  • 죽은 코드 제거
    • 필요 없는 문장을 디지털 화염방사기로 삭제

데이터 조직화

  • 변수 분리
    • 하나의 값이 여러 목적으로 사용되면 혼란과 버그 발견
  • 필드 이름 변경
    • 변수 이름을 잘 짓는 일은 까다로우면서 중요
  • 파생 변수 질의 함수 변경
    • 변수 자체 제거
  • 참조 값 변경
    • 참조인지 값인지 햇갈릴 때 진행
  • 매직 리터럴 변경
    • 코드의 의미를 알기 어려운 리터럴이 보이면 변경

조건부 로직 간소화

  • 조건문 분해, 통합
    • 복잡한 조건문을 분해하고 명확하게 다듬고자 통합
  • 중첩 조건문 보호 구문 변경
    • 함수의 핵심로직 들어가기 전 무언가를 검사할 때 시도
  • 조건부 로직 다형성 변경
    • 똑같은 분기 로직이 여러 곳 등장할 때 시도
  • 특이 케이스 추가
    • Null 특이 케이스 처리가 거의 동일할 때 시도
  • 어서션 추가
    • 프로그램 상태 확인하고 결과에 따라 다르게 동작해야 할 때 시도
  • 제어 플래그 탈출문 변경
    • 제어 플래그로 동작하는 코드를 더 간소화할 때 시도

API 리펙토링

  • 좋은 API
    • 데이터를 갱신하는 함수와 조회만 하는 함수 명확히 구분할 수 있는 구조
  • 질의 함수와 변경 함수 분리
    • 데이터 갱신 함수와 조회 함수가 섞여 있을 때 시도
  • 함수 매개변수화
    • 값 하나 때문에 여러 개로 나뉜 함수를 합칠 때 시도
  • 플래그 인수 제거
    • 매개변수가 함수의 동작 모드만 바꾸면 제거
  • 객체 통째로 이동
    • 데이터 구조가 함수 사이를 넘어가서 많이 흩어질 때 시도
  • 매개변수 질의 함수 변경

  • 세터 제거
    • 객체가 불변이길 원하면 적용
  • 생성자 팩터리 함수 변경
    • 호출자에 새로운 객체를 만들어 반환하려 할 때 시도
  • 함수 명령 변경
    • 함수를 객체로 변환하면서 추출하기 수월
  • 수정된 값 반환
    • 함수 안에서 데이터가 수정되었음을 알리기 용이
  • 오류 코드 예외 변경
    • 오류 코드에 의존하는 방식을 바꿀 때 시도
  • 예외 사전확인 변경
    • 문제가 되는 조건을 함수 호출 전에 검사할 수 있을 때 시도

상속 다루기

  • 메소드, 필드, 생성자 본문 올리기 & 메소드, 필드 내리기
    • 특정 기능을 상속 계층구조의 위나 아래로 옮길 때 시도
  • 타입 코드 서브클래스 변경
    • 필드 값에 따라 동작이 달라져서 서브클래스로 대체하고 싶을 때 시도
  • 서브클래스 제거, 슈퍼클래스 추출
    • 계층 사이 클래스를 추가&제거하는 리펙토링
  • 계층 병합

  • 서브클래스, 슈퍼클래스 위임 변경
    • 잘 못된 곳에서 사용하거나 환경이 변한 경우에 상속을 위임으로 변경
Written on November 9, 2022