3장 개요
- 리팩터링을 언제 시작하고 언제 그만할지를 판단하는 일은 리팩터링의 작동 원리를 아는 것 만큼 중요하다.
- 켄트백은 리팩터링할 시점을
냄새
라는 표현 사용 - 리팩터링이 필요한 코드들에는 일정한 패턴이 있음
- 리팩터링을 언제 멈춰야 하는지는 경험과 직관을 통해 감을 키우자
3.1 기이한 이름 - Mysterious Name
- 코드는
단순하고 명료
하게 작성해야 하고 이를 위한 중요한 요소가이름
이다. - 이름 짓는 건 프로그래밍에서도 가장 어려운 두 가지중 하나임 (필칼튼 캐시무효화, 이름짓기)
- 이름 바꾸는 리팩토링을 가장 많이 사용함 :
함수선언 바꾸기
,변수이름 바꾸기
,필드이름 바꾸기
- 이름만 잘 지어도 문맥을 파악하느라 헤매는 시간을 크게 절약할 수 있다.
- 마땅한 이름이 떠오르지 않으면 설계에 문제가 있을 가능성이 있음
3.2 중복 코드 - Duplicated Code
- 똑같은 코드 구조가 여러 곳에서 반복된다면 하나로 통합하자
- 코드가 중복되면 수정시 비슷한 코드를 주의깊게 봐야하고 모두 수정해야함!
- 두 메서드가 똑같은 표현식을 쓸때
함수 추출하기
- 두 메서드가 비슷하다면
문장슬라이드
후 비슷한 부분을 모아함수 추출하기
시전 - 같은 부모로부터 파생된 서브 클래스에 코드 중복이 있으면 따로 호출되지 않도록
메서드 올리기
3.3 긴 함수 - Long Function
- 오랜기간 잘 활용되는 프로그램은
짧은 함수
로 구성되어 있다. - 코드를 끝없이 위임하는 방식인
간접호출(indirection)
을 통해 코드를 이해하고, 공유하고 선택하기 쉬워진다. - 함수가 짧아지면 개발시 왔다갔다 하는 비용이 발생하나 IDE를 통해 부담을 줄일 수 있다.
- 함수의
이름
을 잘 지어 둘 경우 본문 코드를 볼 이유를 없앨 수 있다. - 주석이 필요한 부분은 함수로 만들어 코드로 녹이고 함수 이름은
의도(intention)
가 드러나게 지어주자 - 짧은 함수는 99%
함수 추출하기
를 통해 만들어진다. - 임시 변수와 매개변수가 너무 많아질 경우 아래 방법으로 줄여주자
임시 변수를 질의함수를 바꾸기
,매개변수 객체만들기
,객체 통째로 넘기기
함수를 명령으로 바꾸기
- 함수 추출 대상 선별
- 주석 : 주석이 설명하는 코드를 함수로 추출
- 조건문 :
조건문 분해하기
, case문 마다함수 추출하기
>조건부로직을 다형성으로 바꾸기
등 - 반복문 : 반복문내부 로직을 추출, 여러 작업이 섞여 있다면
반복문 쪼개기
고려
3.4 긴 매개변수 목록 - Long Parameter List
- 이전에는 전역데이터를 줄이기 위해 함수에 필요한 모든 데이터를 매개변수로 전달했으나 매개변수가 많아지면 함수를 이해하기 힘들어 진다.
- 다른 매개변수에서 값을 얻어오는 매개변수는
매개변수를 질의 함수로 바꾸기
로 제거 - 특정 오브젝트에서 값을 뽑아 각각 전달한다면
객체 통째로 넘기기
- 항상 함께 전달되는 값이 있다면
매개변수 객체로 만들기
여러 함수를 클래스
로 묶기 를 통해 공통 값들을 클래스의 필드로 정의 > 함수형 프로그래밍의부분 적용 함수
3.5 전역 데이터 - Global Data
- 전역변수, 클래스 변수, 싱글톤 같은 전역데이터는 코드 어디서든 수정할 수 있어 버그 찾기가 힘들다
변수 캡슐화하기
를 통해 함수로 감싸서 데이터 수정하는 부분을 쉽게 찾고 접근을 통제할 수 있다.
3.6 가변 데이터 - Mutable Data
- 데이터를 변경할 경우 버그가 발생할 수 있다.
- 그래서 최근의
함수형 프로그래밍
은 원본은 두고 값을 복사해서 연산뒤 반환해주는 패러다임임 변수 캡슐화하기
: 정해놓은 함수로만 값을 수정- 하나의 변수의 용도가 다른 값들을 저장하느라 값을 갱신하는 경우
변수 쪼개기
를 통해 용도별로 독립 변수에 저장 - 값을 갱신하는 로직은
문장슬라이드
,함수 추출하기
를 통해 다른 코드와 분리 질의함수와 변경함수 분리하기
를 통해 부작용이 있는 코드는 호출하지 못하게 하자.- 가능한
세터 제거하기
를 통해 변수 수정의 유효범위를 줄여주자 - 다른 곳에서 값을 설정할 수 있는 경우
파생변수를 질의함수로 바꾸기
를 통해 접근하도록 바꿔줌 여러함수를 클래스로 묶기
,여러함수를 변환함수로 묶기
를 통해 갱신 코드의 유효범위 제한- 구조체일 경우는
참조를 값으로 바꾸기
를 적용하여 값 갱신시 구조체를 통째로 교체하자.
3.7 뒤엉킨 변경 - Divergent change
- 코드를 수정할 때는 시스템에서 교쳐야할 딱 한 군데를 찾아서 그 부분만 수정하는것이 좋다. -
단일책임원칙(SRP - Single responsibility pricsiple)
- 단일책임원칙(SRP)이 지켜지지 않고 하나의 모듈이 서로 다른 이유로 인해 여러가지 방식으로 변경되는 일이 많을 때 발생함
단계 쪼개기
를 통해 DB 연동과 비즈니스 레이어 분리- 전체 과정에서 다른 맥락의 함수 호출 빈도가 높다면
함수 추출하기
와함수 옮기기
,클래스 추출하기
로 적당한 모듈을 만들어서 관련 함수를 모아줌
3.8 산탄총 수술 - Shotgun Surgery
- 뒤엉킨 변경과 비슷하면서도 정반대의 냄새
- 코드를 변경할 때마다 자잘하게 수정해야 하는 클래스가 많을 때 발생
- 함께 변경되는 대상을
함수옮기기
와필드 옮기기
를 통해 한 모듈로 묶어줌 - 비슷한 데이터를 다루는 함수가 많다면
여러 함수를 클래스로 묶기
적용 - 데이터구조를 변환하거나 보강하는 함수들은
여러 함수를 변환함수로 묶기
적용 - 이렇게 묶은 함수들은 출력결과를 묶어서
단계쪼개기
를 통해 다음 단계 로직으로 전달 - 어설프게 분리된 로직은 함수
함수 인라인하기
,클래스 인라인하기
로 다시 합쳐줌 - 코드를 재구성하는 중간과정에서는 큰 덩어리로 뭉쳐지는건 상관이 없다.
3.9 기능 편애 - Feature Envy
- 프로그램을 모듈화 할때는 코드의 영역을 나눈 뒤 영역 안에서의 상호작용은 늘리고 영역간의 상호작용은 줄이는데 주력해야한다.
- 함수가 자기가 속한 모듈의 함수나 데이터보다 다른 모듈과 상호작용을 많이 할때 발생
- 외부 객체의 getter 메서드 호출 :
함수옮기기
를 통해 데이터 근처로 옮겨줌 - 함수 일부에서만 기능 편애할 경우
함수추출하기
로 분해한 후함수옮기기
를 통해 원하는 모듈로 이동 - 함수에서 사용하는 모듈이 많다면 가장 많은 데이터를 포함한 모듈로 이동
- 변경할 대상을 한데 모으는 것 :
전략패턴
,방문자패턴
,자기위임
3.10 데이터 뭉치 - Data Clumps
- 항상 몰려다니는 데이터 뭉치는 보금자리를 따로 마련해줘야 한다.
- 필드형태의 데이터 뭉치는
클래스 추출하기
를 통해 하나의 객체로 묶어줌 - 메서드 시그니처의 데이터 뭉치는
매개변수 객체만들기
,객체 통째로 넘기기
를 사용해 매개변수 수를 줄임
클래스로 옮기는 것을 권장한다.
기능편애
,데이터 뭉치
를 해결하다 나온 클래스들로 옮길 좋은 동작들이 없는지 확인하고 통합하다 보면 유용한 클래스가 나와 생산성 향상에 기여 할 수 있다.
3.11 기본형 집착 - primitive Obsession
- 기본형에 집착하지 말고 상황에 맞는 기초 타입에 맞게 정의해서 사용하자(화폐,좌표,구간,전화번호 등)
기본형을 객체로 바꾸기
를 통해 다양한 기능을 제공하는 객체로 변경- 기본형 코드가 조건부 제어하는 타입코드라면
타입코드를 서브클래스로 바꾸기
,조건부 로직을 다형성으로 바꾸기
적용 - 자주 뭉쳐다니는 기본형 그룹의 데이터 뭉치는
클래스 추출하기
,매개변수 객체 만들기
를 통해 통합
3.12 반복되는 switch문 - Repeated Switches
switch
이나if/else
문을조건부 로직을 다형성으로 바꾸기
를 통해 코드베이스를 세련되게 바꿔주자
3.13 반복문 - Loops
- 현재 일급함수(first-calss function)를 지원하는 언어가 많아졌다 -
함수형프로그래밍
반복문을 파이프라인으로 바꾸기
를 적용해서 반복문을 제거해 주자.- filter, map 등의 파이프라인 연산으로 원소들이 어떻게 처리되는지 쉽게 파악이 가능하다
3.14 성의 없는 요소 - Lazy Element
- 함수(메서드), 클래스, 인터페이스 등의 프로그램 요소가 필요 없을 경우도 있다. (오버엔지니어링)
- 본문코드 그대로 쓰는 경우, 메서드가 하나뿐인 클래스 등
함수 인라인하기
,클래스 인라인하기
로 통합, 상속을 사용했다면계층 합치기
를 적용
3.15 추측성 일반화 - Speculative Generality
나중에 필요할거야
라는 생각으로 당장 필요없는 모든 후킹 포인트와 특이케이스 처리 로직을 작성해 둘 경우 발생- 이해와 관리만 어려워지고 실제로 사용되지 않는다면 낭비일 뿐!
- 하는일이 거의 없는 추상클래스는
계층 합치기
로 제거 - 쓸데 없이 위임하는 코드는
함수 인라인하기
나클래스 인라인하기
로 삭제 - 본문에서 사용되지 않는 매개변수는
함수 선언바꾸기
로 제거 - 테스트코드에서밖에 사용하지 않는 케이스는 테스트케이스 삭제 후
죽은 코드 제거하기
적용
3.16 임시 필드 - Temporary Field
- 특정 상황에서만 값이 설정되는 필드
클래스 추출하기
를 통해 분리하고함수 옮기기
를 통해 관련 코드를 옮겨줌- 임시 필드의 유효성을 체크후 동작하는 조건부 로직은
특이 케이스 추가 하기
로 대안 클래스 생성후 제거
3.17 메시지 체인 - message Chains
- 다른 객체를 요청하는 작업이 연쇄적으로 이어지는 코드
- 클라이언트가 객체 네비게이션 구조에 종속되어 중간 단계가 수정되면 클라이언트 코드 수정 필요
위임 숨기기
로 해결- 최종 객체의 결과 사용처를
함수 추출하기
를 통해 빼낸 뒤함수 옮기기
로 숨겨주자
3.18 중개자 - Middle Man
- 객체의 대표적인 기능으로 외부로부터 세부사항을 숨겨주는
캡슐화(encapsulation)
가 있는데 이를 위해위임(delegation)
이 자주 활용된다. - 절반이상의 메서드를 위임하는 것 처럼 위임이 과할 경우
중개자 제거하기
로 처리하는 객체를 직접 호출하도록 변경 - 위임메서드 제거 후 남은 일이 별로 없다면 호출하는 쪽으로
함수인라인 하기
고려
3.19 내부자 거래 - insider Trading
- 모듈 사이의 데이터 거래가 많으면
결합도(Coupling)
가 높아진다. - 데이터를 주고받는 모듈들이 있다면
함수 옮기기
와필드 옮기기
로 분리해줌 - 여러 모듈이 같은 관심사를 공유하면 제3의 모듈을 만들거나
위임 숨기기
를 통하여 다른 모듈이 중간자 역할을 하게 만들자 - 상속 구조에서 자식클래스에서 부모클래스가 공개한 정보 외에도 접근하려 할 경우
서브클래스를 위임으로 바꾸기
,슈퍼클래스를 위임으로 바꾸기
를 적용
3.20 거대한 클래스 - Large Class
- 한 클래스가 너무 많은 일을 하려다 보면 필드 수와 중복코드가 늘어난다.
클래스 추출하기
로 필드 일부를 따로 묶어서 분리함. ex) 접두어, 접미어가 같은 경우- 상속관계가 더 적절하다면
슈퍼클래스 추출하기
,타입코드를 서브클래스
로 바꾸기 적용 - 이 과정에서 클래스가 너무 많아 지지 않도록
중복 코드 제거
도 같이 병행해준다.
3.21 서로 다른 인터페이스의 대안 클래스들 Alternative Classes with Different Interfaces
- 클래스의 장점은 언제든 다른 클래스로 교체할 수 있다는 점이다.(
동일 인터페이스
일 경우) 함수 선언 바꾸기
를 통해 메서드 시그니처를 일치 시키고함수옮기기
를 통해 인터페이스가 같아질 때까지 클래스에 통합시킴- 이후 대안 클래스들 사이에 중복코드가 생기면
슈퍼클래스 추출하기
적용 고려
3.22 데이터 클래스 - Data Class
Data Class
란 데이터 필드와 getter / setter 로만 이루어진 클래스이다.- public 필드는
레코드 캡슐화 하기
적용 - 변경하면 안되는 필드는
세터 제거하기
적용하여 접근 봉쇄 - 데이터 클래스를 많이 사용하는 메서드를
함수 추출하기
,함수 옮기기
를 통해 데이터 클래스 내부로 옮길 수 있을지 검토 - 불편 필드는 굳이 캡슐화 하지 않고 필드 자체를 공개해도 무방하다.
3.23 상속 포기 - Refused Bequest
- 서브클래스에서 부모의 모든 정보가 아닌 관심사 몇개만 필요할 경우가 있다.
- 같은 계층에 서브클래스를 만들고
메서드 내리기
,필드내리기
를 활용해서 물려받지 않을 부분을 새로만든 서브클래스로 옮겨서 부모에 공통 부분만 남겨주자. - 부모 클래스는 항상
추상클래스
여야 한다고 말하는 사람도 많다. - 예전에는 계층구조를 잘못 설계한 케이스라고 했지만 실무에서 유용하기에 항상 이렇게 변경 할 필요는 없다는 의견이다.
- 서브클래스가 부모의 동작은 필요로 하지만 인터페이스는 따르고 싶지 않을때 발생
- 인터페이스를 따르지 않을 경우
서브클래스를 위임으로 바꾸기
,슈퍼클래스를 위임으로 바꾸기
적용해서 상속에서 벗어나자
3.24 주석 - Comments
주석
이 장황하게 달린 원인은 코드가 잘못 작성 됐기 때문일 경우가 많음- 리팩터링을 통해 냄새를 걷어내면 주석이 필요 없어지는 경우들이 많다.
- 특정 블록이 하는일에 주석을 남기고 싶다면
함수 추출하기
적용 - 추출한 함수에 여전히 설명이 필요하다면
함수 선언 바꾸기
로 더 의미있는 이름으로 변경 - 선행조건을 명시하고 싶다면
어서션 추가하기
적용
주석을 남겨야겠다는 생각이 들면, 가장 먼저 주석이 필요 없는 코드로 리팩터링해본다.
Reference
마틴파울러 - 리팩터링2판
'Program > Refactoring' 카테고리의 다른 글
[Refactoring] 5. 리팩터링 카탈로그 보는 법 (1) | 2022.10.23 |
---|---|
[Refactoring] 4. 테스트 구축하기 (0) | 2022.10.23 |
[Refactoring] 2. 리팩터링 원칙 (0) | 2022.08.18 |
[Refactoring] 1. 리팩터링: 첫 번째 예시 (0) | 2022.08.18 |