Refactoring

[리팩터링 2판] 6장

ㅋ. ㅋ 2024. 1. 17. 14:30

6 기본적인 리팩터링

  • 함수 추출하기 <-> 함수 인라인하기
  • 변수 추출하기 <-> 변수 인라인하기
  • 함수 선언 바꾸기 : 함수 이름 바꾸기
  • 변수 이름 바꾸기 --> 변수 캡슐화하기와 관련이 깊음
  • 매개변수 객체 만들기 : 같이 다니는 인수들 묶기
  • 여러 함수를 클래스로 묶기 : 함수를 만들고 고수준 모듈로 묶기
  • 여러 함수를 변환 함수로 묶기 : 읽기전용 데이터 다루기 좋음
  • 단계 쪼개기 : 묶인 모듈들의 작업 처리 과정을 명확한 단계로 구분짓기

6.1 함수 추출하기

코드를 언제 함수로 묶어야하는가

  • 길이 , 재사용성 : 두번 이상 사용 되는 코드
  • 목적과 구현을 분리 : 기능 파악이 어렵다면 함수로 추출하기

함수 내 코드 대여섯줄 정도 짧은 함수 작성 --> 이름 잘 짓기 !!!

절차

  1. 함수를 새로 만들고 목적을 잘 드러내는 이름 붙이기 (무엇을!! 하는지, not 어떻게)
    • 이름이 안 떠오른다면 함수로 추출하면 안 될수도
  2. 추출할 코드를 원본 함수에서 복사하여 새 함수에 붙여넣는다
  3. 추출한 코드 중 원본 함수의 지역변수를 참조하거나, 추출한 함수의 유효범위를 벗어나는 변수 검사 -> 있다면 매개변수로 전달
    • 중첩 함수 사용시 이런 문제 x
    • 매개 변수 중 값이 변하는 것은 주의, 변하는 값이 하나라면 질의함수 취급해서 그 결과를 변수에 대입 ......?
    • 지역변수가 너무 많으면 변수 쪼개기나 임시변수를 질의함수로 바꾸기 적용해서 코드 단순화한 후 추출 재시도
  4. 변수를 다 처리했으면 컴파일
  5. 원본 함수에서 함수 호출
  6. 테스트
  7. 다른 코드에 방금 추출한 함수랑 비슷하거나 같은 곳 없는지 확인 후 있다면 새 함수를 호출하도록 바꿀지 검토 (인라인 코드를 함수 호출로 바꾸기)

값을 반환할 변수가 여러개라면 ?
- 추출할 코드를 다르게 재구성하는 방향으로 처리함
- 함수가 값 하나만 반환하는 방식을 선호하기 때문에 각각 반환하는 함수 여러개 만듦
- 굳이 한 함수에서 여러개 반환하고 싶으면 값을 레코드로 묶어서 반환하는 것도 괜찮지만
- 임시 변수 추출 작업을 다른 방식으로 처리하는 것이 나을 때가 많다 (임시 변수를 질의 함수로 바꾸거나 변수를 쪼개는 것과 같이..임시변수를 질의함수로 바꾸는게 뭐야?)

추출함수를 최상위수준 같은 다른 문맥으로 이동할 때는 일단 그 함수 내에서 추출하고 다른데로 옮겨보기, 변수 처리가 힘들 것이라 예상하지만 막상 해봐야 알 수 있음

6.2 함수 인라인하기

메서드 내용 직접 삽입

본문 함수가 깔끔할 때는 간접 호출 대신 함수 인라인하기

잘못 추출 된 함수들도 인라인 하고 간접 호출 너무 과해도 인라인하기

절차

  1. 다형 메서드인지 확인
    • 서브클래스에서 오버라이드 하는 메서드는 인라인 하면 안됨
    • 이게 무슨 뜻일까?? ex 필요
  2. 인라인할 함수를 호출하는 곳을 모두 찾기
  3. 각 호출문을 함수 본문으로 교체
  4. 하나 교체 하고 테스트 반복
  5. 원래 함수 삭제

상황이 복잡하면 인라인 하지말기~

항상 단계를 잘게 나누기

6.3 변수 추출하기

표현식이 너무 복잡해서 이해 하기 어려울 때 변수로 추출

현재 함수 안에서만 의미가 있다면 변수로 추출하는 것이 좋음

함수를 벗어난 넓은 문맥에서 사용된다면 변수가 아닌 함수로 추출해야함

절차

  1. 추출하려는 표현식에 부작용 없는지 확인 (부작용이 뭐임?)
  2. 불변 변수를 선언하고 표현식의 복제본 대입
  3. 원본 표현식을 새로 만든 변수로 교체
  4. 테스트
  5. 표현식을 여러곳에서 사용 시 다 새로 만든 변수로 교체, 교체 시 마다 테스트 하기

6.4 변수 인라인하기

절차

  1. 대입문의 우변 (표현식) 부작용 여부 확인
  2. 변수가 불변으로 선언되지 않았다면 불변으로 만든 후 테스트
    • 변수에 값이 한 번만 대입되는지 확인 가능
  3. 이 변수를 가장 처음 사용하는 코드 찾아서 표현식으로 변경
  4. 테스트
  5. 교체 - 테스트 반복
  6. 변수 선언문, 대입문 삭제
  7. 테스트

6.5 함수 선언 바꾸기

좋은 함수명 - 먼저, 함수의 목적을 설명해보기

매개변수 올바르게 선택하기 - 정답이 없음

절차

  • 간단한 절차
  1. 매개변수 제거 시, 함수 본문에 제거 대상 매개 변수 참조하는 곳 없는지 확인
  2. 메서드 선언을 원하는 형태로 변경
  3. 기존 메서드 선언을 참조하는 부분을 모두 찾아 바뀐 형태로 수정
  4. 테스트
  • 마이그레이션 절차
  1. 함수 본문을 적절히 리팩터링하기
  2. 함수 본문을 새로운 함수로 추출
    • 새로 만들 함수 이름이 기존함수와 같다면 일단 임시 이름 붙여주기
  3. 추출한 함수에 매개변수를 추가해야한다면 간단한 절차에 맞춰 추가
  4. 테스트
  5. 기존 함수를 인라인
    • 기존 함수 내용 다 묶어서 새 임시 함수 만들고 기존 함수에서 새함수 호출
  6. 임시로 붙인 이름을 함수 선언 바꾸기를 통해 원래 이름으로 되돌림
  7. 테스트

매개변수 추가 시 assert를 통해 추가된 매개변수를 실제로 사용하는지 확인

6.6 변수 캡슐화 하기

접근 범위가 넓은 데이터는 접근에 제한을 걸어주면 좋음

절차

  1. 변수로 접근, 갱신하는 전담 캡슐화 함수 만들기
  2. 정적 검사 수행 (어떻게?)
  3. 변수를 직접 참조하던 부분을 모두 캡슐화 함수 호출로 변경하고 테스트
  4. 변수의 접근 범위 제한
    • 변수로 직접 접근을 막지 못할 때는 변수 이름을 바꿔서 테스트해보면 해당 변수를 참조하는 곳을 찾아낼 수 있음
  5. 테스트
  6. 변수 값이 레코드라면 레코드 캡슐화하기 적용 고려

6.7 변수 이름 바꾸기

명확한 프로그래밍의 핵심은 이름 짓기

절차

  1. 폭넓게 쓰이는 변수라면 캡슐화하기를 고려
  2. 이름을 바꿀 변수를 참조하는 곳 모두 찾아 변경
    • 다른 코드베이스에서 참조하는 변수는 외부에 공개된 변수이므로 해당 리팩터링 적용 불가 (?)
    • 변수값이 변하지 않는다면 다른 이름으로 복제본으로 만들어 하나씩 점진적으로 변경 후 테스트
  3. 테스트

6.8 매개변수 객체 만들기

데이터 항목 여러개를 데이터 구조로 모아 매개변수로 만들기

절차

  1. 적절한 데이터 구조 만들기
  2. 테스트
  3. 함수선언바꾸기 (매개변수 수정) 로 새 데이터 구조를 매개변수로 추가
    • const exampleFunction = (originalData1, originalData2, newDataObject) => { ... }
  4. 테스트
  5. 함수 호출 시 데이터 구조 인스턴스 넘기도록 수정
    • exampleFunction (originalData1, originalData2, newDataObject);
    • 하나 고치고 하나 테스트
  6. 기존 매개변수를 사용하던 코드를 새 데이터 구조의 원소를 사용하도록 변경
    • exampleFunction (newDataObject);
  7. 다 바꿨다면 기존 매개변수를 제거 하고 테스트

6.9 여러 함수를 클래스로 묶기

공통된 데이터를 사용하는 함수들을 새 클래스에 모으기
- 클라이언트가 객체의 핵심 데이터를 변경 가능
- 파생객체 일관되게 관리 가능
함수를 묶는 다른 방법 : 여러함수를 변환함수로 묶기

절차

  1. 함수들이 공유하는 공통 데이터 레코드를 캡슐화
    • 공통 데이터가 레코드 구조로 묶여있지 않다면 매개변수 객체 만들기를 통해 데이터를 하나로 만들기
  2. 공통 레코드를 사용하는 함수 각각을 새 클래스로 옮기기 (함수 옮기기)
  3. 데이터 조작 로직들은 함수로 추출해 새 클래스로 옮기기

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

원본데이터가 코드 안에서 갱신 시 클래스로 묶기

변환 함수로 묶을 시 가공 데이터를 새 레코드에 저장하므로 원본 데이터가 깨짐

function wonToDollar (money) {...}
function wonToEuro (money) {...}

--> function wonToMoney (money) {
const result = _.cloneDeep(money);
result .euro = wonToEuro(result);
result .dollar = wonToDollar(result);
... // 여기에 중첩 함수로 wonTo~ 함수들 깔려있고
return result ;
}
// 이런 느낌인가?

절차

  1. 변환할 레코드를 입력 받아 값을 그대로 반환하는 변환함수 만들기
    • 깊은 복사로 처리
  2. 묶을 함수 중 함수 하나를 골라 본문 코드를 변환 함수로 옮기고, 처리 결과를 레코드에 새 필드로 기록. 그 후 클라이언트 코드가 이 필드를 사용하도록 수정
  3. 테스트
  4. 반복

6.11 단계 쪼개기

서로 다른 두 대상을 다루면 쪼개기
다른 단계에 있는 코드들 쪼개기

절차

  1. 두번 째 단계에 해당하는 코드를 독립 함수로 추출
  2. 1. 양치하는 함수 2. 세수하는 함수 <-- 이게 두번 째 단계 코드?
  3. 테스트
  4. 중간 데이터 구조를 만들어 앞에서 추출한 함수의 인수로 추가
  5. 테스트
  6. 추출한 두번 째 단계 함수의 매개변수를 하나씩 검토 (ex. 세수), 그 중 첫번째 단계에서 사용되는 것은 중간 데이터 구조로 옮김. 하나씩 옮기면서 테스트
  7. 첫번째 단계코드를 함수로 추출하면서 중간 데이터 구조를 반환하도록 만들기

'Refactoring' 카테고리의 다른 글

[리팩터링 2판] 8장  (4) 2024.02.28
[리팩터링 2판] 7장  (0) 2024.01.17
[리팩터링 2판] 3, 4장  (0) 2023.10.04
[리팩터링 2판] 2장  (0) 2023.02.21
[리팩터링 2판] 1장  (0) 2023.02.09