-
[Refactoring] 3장 : 코드에서 나는 악취책/Refactoring 2판 2024. 2. 14. 11:22반응형
기이한 이름
- 함수, 변수, 클래스 등은 이름만 보고도 무슨 일을 할지 알 정도로 잘 써야 한다.
- 함수 선언 바꾸기, 변수 이름 바꾸기, 필드 이름 바꾸기
중복 코드
- 코드가 여러 군데에 똑같은게 있다면 하나로 통합하자
- 두 메서드가 똑같은 표현식 사용 → 함수 추출하기 를 통해 양쪽 모두 추출한 메서드를 호출하도록
- 비슷한 표현식 → 문장 슬라이드하기 를 통해 비슷한 부분을 한 곳에 모아 함수 추출
- 서브 클래스들에 코드 중복 → 메서드 올리기 를 적용하여 부모로 옮김
긴 함수
- 함수는 잘게 쪼개는 것이 좋다 ← 이때 주의할 점은 함수의 이름이 반드시 명확해야 함
- 함수 내부의 코드의 목적을 분명하게 나타내줘야함
- 그래야만 함수를 잘게 쪼개는 의미가 있다.
- 함수 추출하기
- 임시 변수 줄이기 - 임시 변수를 질의 함수로 바꾸기
- 매개 변수 줄이기 - 매개변수 객체 만들기, 객체 통째로 넘기기
- 위를 다 적용해도 임시 변수와 매개변수가 많다면 함수를 명령으로 바꾸기
- 조건문 - 조건문 분해하기
- switch 문 → case 마다 함수 추출하기 - 각 case의 본문을 함수 호출문 하나로 바꿈
- 같은 조건을 기준으로 나뉘는 switch 문이 여러개라면 조건문을 다형성으로 바꾸기
- 반복문 - 반복문 쪼개기
긴 매개변수 목록
- 다른 매개변수에서 값을 얻을 수 있는 매개변수 ← 매개변수를 질의 함수로 바꾸기
- 사용 중인 데이터 구조에서 값 뽑아 각각을 매개변수로 제공한다면 → 객체 통째로 넘기기
- 항상 함께 전달되는 매개변수 하나로 묶기 → 매개변수 객체 만들기
- 플래그 역햘의 매개변수 제거하기 → 플래그 인수 제거하기
- 여러 함수가 특정 매개변수들의 값을 공통으로 사용 → 여러 함수를 클래스로 묶기
전역 데이터
- 그냥 쓰지 말자
- 전역 데이터를 감싸기 → 변수 캡슐화하기
- 클래스 변수, 싱글톤에서도 전역 변수와 비슷한 문제 발생 가능
가변 데이터
- 데이터가 예상치 못하게 바뀌는 경우가 많음
- 함수형 프로그래밍에서는 데이터가 절대 변하지 않고, 데이터를 변경하려면 반드시 원래 데이터를 그대로 둔 채 변경된 복사본을 리턴하게 된다.
- 변수 캡슐화하기 → 정해놓은 함수를 거쳐야만 값을 수정하도록
- 하나의 변수에 용도가 다른 값들을 저장하느라 값 갱신하는 경우 → 변수 쪼개기
- 갱신 로직을 다른 코드와 떨어뜨려놓기 → 문장 슬라이드하기, 함수 추출하기
- API 만들때 부작용이 있는 코드를 호출할 수 없게 → 질의 함수와 변경 함수 분리하기
- 세터 가급적 제거하기 → 세터 제거하기
뒤엉킨 변경
- 단일 책임 원칙 (SRP)이 제대로 지켜지지 않을 떄 나타남
- 한 코드에 섞여 들어감
- 무언가 추가될 때마다 함수 몇개가 동시에 바뀌어야 하는 경우
- 순차적으로 실행되는 게 자연스러운 맥락이면 다음 맥락에 필요한 데이터를 특정 데이터 구조에 담아 전달 → 단계 쪼개기
- 전체 처리 과정 곳곳에 각기 다른 맥락의 함수를 호출하는 빈도가 높다 → 각 맥락에 해당하는 모듈을 만들어서 관련 함수를 모음 (함수 옮기기)
- 이 과정에서 여러 맥락의 일에 관여하는 함수가 있다면 함수 추출하기
- 모듈이 클래스라면 클래스 추출하기
→ 맥락별로 분리
산탄총 수술
- 코드를 변경할 때마다 자잘하게 수정해야 하는 클래스가 많아질 때
- 여러 코드에 흩뿌려짐
- 함께 변경되는 대상들을 한 모듈에 묶음 → 함수 옮기기, 필드 옮기기
- 비슷한 데이터를 다루는 함수가 많다면 → 여러 함수를 클래스로 묶기
- 데이터 구조를 변환하거나 보강하는 함수 → 여러 함수를 변환 함수로 묶기
- 이렇게 묶은 함수들의 출력 결과를 묶어서 다음 단계 로직으로 전달할 수 있다 → 단계 쪼개기
- 어설프게 분리된 로직 → 함수 인라인하기, 클래스 인라인하기
→ 맥락별로 모음
기능 편애
- 어떤 함수가 자신이 속한 모듈의 함수나 데이터보다 다른 모듈의 함수나 데이터와 더 상호작용을 많이 하는 경우
- 이 함수를 데이터 근처로 옮겨줌 → 함수 옮기기
- 함수의 일부에서만 기능을 편애 → 함수 추출하기 로 독립 함수로 뺀 다음 원하는 모듈로 보냄 (함수 옮기기)
- 함수 추출하기 로 함수를 여러 조각으로 나누고 각각을 적합한 모듈로 옮긴다.
데이터 뭉치
- 몰려다니는 데이터 뭉치를 위한 클래스를 만들어줘야 한다.
- 필드 형태의 데이터 뭉치 → 클래스 추출하기 로 하나의 객체로 묶음
- 메소드 시그니처 → 매개변수 객체 만들기, 객체 통째로 넘기기
기본형 집착
- 전화번호와 같이 단순한 문자열로는 표현이 아쉬운 경우, 이를 객체로 만들어 표현한다.
- 기본형을 객체로 바꾸기
- 기본형으로 표현된 코드가 조건부 동작 제어하는 타입 코드라면 → 타입 코드를 서브클래스로 바꾸기, 조건부 로직을 다형성으로 바꾸기
- 자주 몰려다니는 기본형 그룹 → 클래스 추출하기, 매개변수 객체 만들기
반복되는 switch문
- 똑같은 조건부 로직이 여러 곳에서 반복되는 경우
- switch나 if를 무조건 없애는 게 아니고, 다른 코드에서 반복되는 로직이 있을 경우 → 조건부 로직을 다형성으로 바꾸기
반복문
- 반복문은 파이프라인을 사용하자 → 반복문을 파이프라인으로 바꾸기
성의 없는 요소
- 본문 코드를 그대로 쓰는 것과 다름없는 함수, 메서드가 하나뿐인 클래스
- 역할을 제대로 하지 못하는 클래스, 함수 등은 인라인으로 보내주는 것 → 함수 인라인하기, 클래스 인라인하기
- 상속 계층의 경우 계층을 합쳐버린다. → 계층 합치기
추측성 일반화
- 미래에 사용될 것으로 예측해 쓸데없이 적어놓은 코드
- 하는 일이 없는 추상 클래스 → 계층 합치기
- 쓸데없이 위임하는 코드 → 함수 인라인하기, 클래스 인라인하기
- 본문에서 사용되지 않는 매개변수 → 함수 선언 바꾸기
- 테스트 코드 말고는 사용하지 않는 함수, 클래스 → 테스트 케이스 제거한 뒤 죽은 코드 제거하기
임시 필드
- 특정 상황에서만 값이 채워지는 필드
- 보통 필드는 다 채워질 것이라고 기대하므로 추출해내야함
- 클래스 추출하기 → 함수 옮기기 : 임시 필드를 사용하는 함수들을 모조리 새 클래스에 넣는다.
- 임시 필드의 유효성을 검사하고 동작하는 조건부 로직 → 특이 케이스 추가하기 로 필드들이 유효하지 않을 때를 위한 대안 클래스를 만들어 제거
메시지 체인
- this.property.name.first 등 .으로 쭉 이어지는 경우
- 중간 단계를 숨겨줌으로써 해결 가능 → 위임 숨기기
- 이러면 중간 객체들이 모두 중개자가 되어버림
- 최종 결과 객체가 어떻게 쓰이는지 확인
- 함수 추출하기 로 결과 객체를 사용하는 코드 일부를 빼내어 함수 옮기기 로 체인을 숨길 수 있는지 확인
중개자
- 하는 일 없이 중개만 하는 코드들을 제거해준다.
- 중개자 제거하기 → 직접 소통하도록 만들어줌
- 위임 메서드를 제거하고 남는 일이 거의 없다 → 호출하는 쪽으로 함수 인라인하기
내부자 거래
- 모듈 사이에 데이터 거래가 많은 경우 이를 최소로 줄여야함
- 함수 옮기기, 필드 옮기기 로 내부 거래 떼어놓음
- 여러 모듈이 같은 관심사 공유 → 제 3의 모듈을 만들거나 위임 숨기기
- 상속 → 서브클래스를 위임으로 바꾸기, 슈퍼클래스를 위임으로 바꾸기
거대한 클래스
- 너무 큰 클래스는 필드 중복이 생기기 쉽다.
- 필드들 일부를 따로 묶음 → 클래스 추출하기
- 이렇게 분리한 컴포넌트들이 상속 관계여야 한다면 → 슈퍼클래스 추출하기, 타입 코드를 서브클래스로 바꾸기
- 클래스 안에서 유용한 기능 그룹을 찾았다면 → 클래스 추출하기, 슈퍼클래스 추출하기, 타입 코드를 서브클래스로 바꾸기
서로 다른 인터페이스의 대안 클래스들
- 클래스의 장점 : 언제든 다른 클래스로 바꿀 수 있다. 단, 인터페이스가 같아야 함
- 메서드 시그니처 일치시켜야함 → 함수 선언 바꾸기
- 부족하다면 인터페이스가 같아질 때까지 필요 동작들을 클래스 안으로 밀어넣음 → 함수 옮기기
- 대안 클래스 사이에 중복 코드가 생기면 슈퍼클래스 추출하기 고려
데이터 클래스
- 게터/세터 및 필드로만 이루어진 클래스
- 변하지 않아야 하는 필드에서는 세터를 없앤다. → 세터 제거하기
- public 필드 → 레코드 캡슐화하기
- 다른 클래스의 메서드에서 데이터 클래스의 게터, 세터 자주 사용 → 함수 옮기기로 그 메서드를 데이터 클래스로 옮길 수 있는지
- 통째로 옮기기 어렵다면 일부를 함수 추출하기 한 뒤 그 부분만 별도 메서드로 뽑아냄
상속 포기
- 물려받고 싶지 않는 부모 필드, 메서드는 → 메서드 내리기, 필드 내리기 로 모두 서브클래스로 옮겨줌
- 서브클래스가 부모의 동작은 필요로 하지만 인터페이스는 따르고 싶지 않을 경우 → 서브클래스를 위임으로 바꾸기, 슈퍼클래스를 위임으로 바꾸기
주석
- 주석을 남기는 것은 좋으나, 과도해선 안된다
- 주석이 필요없도록 함수로 추출해내는 것이 중요 → 함수 추출하기
- 여전히 설명이 필요하다 → 함수 선언 바꾸기
- 시스템 동작 선행조건 명시하고 싶다 → 어서션 추가하기
반응형'책 > Refactoring 2판' 카테고리의 다른 글
[Refactoring] 10장 : 조건부 로직 간소화 (1) 2024.02.14 [Refactoring] 9장 : 데이터 조직화 (0) 2024.02.14 [Refactoring] 8장 : 기능 이동 (1) 2024.02.14 [Refactoring] 7장 : 캡슐화 (0) 2024.02.14 [Refactoring] 6장 : 기본적인 리팩터링 (0) 2024.02.14