[Refactoring] 3장 코드에서 나는 악취
리팩터링이 필요한, 때로는 아주 절실한 코드들에는 일정한 패턴이 있다.
3.1 기이한 이름
코드를 명료하게 표현하는 데 가장 중요한 요소 하나는 바로 ‘이름’이다.
함수, 모듈, 변수, 클래스 등은 그 이름만 보고도 각각의 용도를 명확히 알 수 있어야 한다.
관련 리팩터링
함수 선언 바꾸기(함수 이름을 바꿀때도 사용한다), 변수 이름 바꾸기, 필드 이름 바꾸기
3.2 중복 코드
똑같은 코드 구조가 여러 곳에서 반복된다면 하나로 통합하여 더 나은 프로그램을 만들 수 있다.
관련 리팩터링
함수 추출하기, 문장 슬라이드하기, 메서드 올리기
3.3 긴 함수
함수는 길수록 이해하기 어렵다. 적극적으로 함수를 쪼개야 한다.
주석을 달아야 할 만한 부분은 무조건 함수로 만든다.
‘무엇을 하는지’를 코드가 잘 설명해주지 못할수록 함수로 만드는게 유리하다.
관련 리팩터링
함수 추출하기, 임시 변수를 질의 함수로 바꾸기, 매개변수 객체 만들기, 객체 통째로 넘기기, 함수를 명령으로 바꾸기, 조건문 분해하기, 함수 추출하기, 조건부 로직을 다형성으로 바꾸기, 반복문 쪼개기
3.4 긴 매개변수 목록
매개변수 목록이 길어지면 그 자체로 이해하기 어려울 때가 많다.
관련 리팩터링
매개변수를 질의 함수로 바꾸기, 객체 통째로 넘기기, 매개변수 객체 만들기, 플래그 인수 제거하기, 여러 함수를 클래스로 묶기
3.5 전역 데이터
전역 데이터는 코드베이스 어디에서든 건드릴 수 있고 값을 누가 바꿨는지 찾아낼 메커니즘이 없다는 게 문제다.
데이터를 함수로 감싸는 것만으로도 데이터를 수정하는 부분을 쉽게 찾을 수 있고 접근을 통제할 수 있게 된다.
관련 리팩터링
변수 캡슐화하기
3.6 가변 데이터
데이터를 변경했더니 예상치 못한 결과나 골치 아픈 버그로 이어지는 경우가 종종 있다.
변수 캡슐화하기 를 적용하여 정해놓은 함수를 거쳐야만 값을 수정할 수 있도록 하면 값이 어떻게 수정되는지 감시하거나 코드를 개선하기 쉽다.
변수를 갱신하는 코드들의 유효범위를 제한해야 한다.
관련 리팩터링
변수 캡슐화하기, 변수 쪼개기, 문장 슬라이드하기, 함수 추출하기, 질의 함수와 변경 함수 분리하기, 세터 제거하기, 파생 변수를 질의 함수로 바꾸기, 여러 함수를 클래스로 묶기, 여러 함수를 변환 함수로 묶기, 참조를 값으로 바꾸기
3.7 뒤엉킨 변경
뒤엉킨 변경은 단일 책임 원칙이 제대로 지켜지지 않을 때 나타난다.
예컨대 지원해야 할 데이터베이스가 추가될 때마다 함수 세 개를 바꿔야 하고, 금융 상품이 추가될 때마다 또 다른 함수 네 개를 바꿔야 하는 모듈이 있다면 뒤엉킨 변경이 발생했다는 뜻이다.
데이터 베이스 연동과 금융 상품 처리는 서로 다른 맥락에서 이뤄지므로 독립된 모듈로 분리해야 프로그래밍이 편하다.
관련 리팩터링
단계 쪼개기, 함수 옮기기, 함수 추출하기, 클래스 추출하기
단일 책임 원칙
‘단일 모듈은 변경의 이유가 하나, 오직 하나여야만 한다’는 설계 원칙이다.
3.8 산탄총 수술
산탄총 수술은 뒤엉킨 변경과 비슷하면서도 정반대다.
코드를 변경할 때마다 자잘하게 수정해야 하는 클래스가 많을 때 산탄총 수술이 발생했다는 뜻이다.
변경할 부분이 코드 전반에 퍼져 있다면 찾기도 어렵고 꼭 수정해야 할 곳을 지나치키 쉽다.
관련 리팩터링
함수 옮기기, 필드 옮기기, 여러 함수를 클래스로 묶기, 여러 함수를 변환 함수로 묶기, 단계 쪼개기, 함수 인라인하기, 클래스 인라인하기
3.9 기능 편애
기능 편애는 흔히 어떤 함수가 자기가 속한 모듈의 함수나 데이터보다 다른 모듈의 함수나 데이터와 상호작용 할 일이 더 많을 때 풍기는 냄새다.
다행히 해결하기는 쉽다. 함수를 데이터 근처로 옮겨주면 된다.
때로는 함수의 일부에서만 기능을 편애할 수 잇다. 이럴 때는 그 부분만 독립 함수로 뺀 다음 원하는 모듈로 보내준다.
항상 기본이 되는 원칙은 ‘함께 본경할 대상을 한데 모으는 것’이다.
관련 리팩터링
함수 옮기기, 함수 추출하기
3.10 데이터 뭉치
몰려다니는 데이터 뭉치는 보금자리를 따로 마련해줘야 마땅하다.
데이터 뭉치인지 판별하려면 값 하나를 삭제해보자. 그랬을 때 다머지 데이터만으로는 의미가 없다면 객체로 환생하길 갈망하는 데이터 뭉치라는 뜻이다.
관련 리팩터링
클래스 추출하기, 매개변수 객체 만들기, 객체 통째로 넘기기
3.11 기본형 집착
프로그래머 중에는 자신에게 주어진 문제에 딱 맞는 기초 타입(화폐, 좌표, 구간 등)을 직접 정의하기를 몹시 꺼리는 사람이 많다.
이 냄새는 문자열을 다루는 코드에서 특히 흔하다.
관련 리팩터링
기본형을 객체로 바꾸기, 타입 코드를 서브클래스로 바꾸기, 조건부 로직을 다형성으로 바꾸기, 클래스 추출하기, 매개변수 객체만들기
3.12 반복되는 switch문
중복된 switch문이 문제가 되는 이유는 조건절을 추가할 때마다 다른 switch 문들도 모두 찾아서 함께 수정해야 하기 때문이다.
관련 리팩터링
조건부 로직을 다형성으로 바꾸기
3.13 반복문
일급함수를 지원하는 언어가 많아졌기 때문에 ‘반복문을 파이프라인으로 바꾸기’를 적용해서 반복문을 제거할 수 있다.
필터나 맵 같은 파이프라인 연산을 사용하면 코드에서 각 원소들이 어떻게 처리되는지 쉽게 파악할 수 있다.
관련 리팩터링
반복문을 파이프라인으로 바꾸기
3.14 성의 없는 요소
함수 추출하기 등을 통해 프로그램을 요소화 했을때 발생할 수 있다.
본문 코드를 그대로 쓰는 것과 진배없는 함수도 있고, 실질적으로 메서드가 하나뿐인 클래스도 있다.
이런 프로그램 요소는 고이 보내드리는게 좋다.
관련 리팩터링
함수 인라인하기, 클래스 인라인하기, 계층 합치기
3.15 추측성 일반화
이 냄새는 ‘나중에 필요할 거야’라는 생각으로 당장은 필요 없는 모든 종류의 후킹 포인트와
특이 케이스 처리 로직을 작성해둔 코드에서 풍긴다.
미래를 대비해 작성하 ㄴ부분을 실제로 사용하게 되면 다행이지만, 그렇지 않는다면 쓸데없는 낭비일 뿐이다. 당장 치워버리자.
관련 리팩터링
계층 합치기, 함수 인라인하기, 클래스 인라인하기, 함수 선언 바꾸기, 죽은 코드 제거하기
3.16 임시 필드
간혹 특정 상황에서만 값이 설정되는 필드를 가진 클래스도 있다.
이렇게 임시 필드를 갖도록 작성하면 코드를 이해하기 어렵다.
관련 리팩터링
클래스 추출하기, 함수 옮기기, 특이 케이스 추가하기
3.17 메시지 체인
메시지 체인은 클라이언트가 한 객체를 통해 다른 객체를 얻은 뒤 방금 얻은 객체에 또 다른 객체를 요청하는 식으로, 다른 객체를 요청하는 작업이 연쇄적으로 이어지는 코드를 말한다.
관련 리팩터링
위임 숨기기, 함수 추출하기, 함수 옮기기
3.18 중개자
클래스가 제공하는 메서드 중 절반이 다른 클래스에 구현을 위임하고 있다면, ‘중개자 제거하기’를 활용하여 실제로 일을 하는 객체와 직접 소통하게 하자.
관련 리팩터링
중개자 제거하기, 함수 인라인하기
3.19 내부자 거래
모듈 사이의 데이터 거래 양을 최소로 줄이고 모두 투명하게 처리해야 한다.
관련 리팩터링
함수 옮기기, 필드 옮기기, 위임 숨기기, 서브클래스 위임으로 바꾸기, 슈퍼클래스를 위임으로 바꾸기
3.20 거대한 클래스
한 클래스가 너무 많은 일을 하려다 보면 필드 수가 상당히 늘어난다. 그리고 클래스에 필드가 너무 많으면 중복 코드가 생기기 쉽다.
관련 리팩터링
클래스 추출하기, 슈퍼클래스 추출하기, 타입 코드를 서브클래스로 바꾸기
3.21 서로 다른 인터페이스의 대안 클래스들
클래스를 사용할 때의 큰 장점은 필요에 따라 언제든 다른 클래스로 교체할 수 있다는 것이다. 단, 교체하려면 인터페이스가 같아야 한다. 따라서 ‘함수 선언 바꾸기’로 메서드 시그니처를 일치시킨다.
관련 리팩터링
함수 선언 바꾸기, 함수 옮기기, 슈퍼클래스 추출하기
3.22 데이터 클래스
데이터 클래스란 데이터 필드와 게터/세터 메서드로만 구성된 클래스를 말한다.
한편, 데이터 클래스는 피룡한 동작이 엉뚱한 곳에 정의돼 있다는 신호일 수 있다.
관련 리팩터링
레코드 캡슐화하기, 세터 제거하기, 함수 옮기기, 함수 추출하기, 단계 쪼개기
3.23 상속 포기
서브 클래스는 부모로부터 메서드와 데이터를 물려받는다. 하지만 부모의 유산을 원치 않거나 필요 없다면?
관련 리팩터링
서브클래스를 위임으로 바꾸기, 슈퍼클래스를 위임으로 바꾸기
3.24 주석
주석은 악취가 아닌 향기를 입힌다.
문제는 주석을 탈취제처럼 사용하는 데 있다.
주석이 장황하게 달린 원인이 코드를 잘못 작성했기 때문인 경우가 의외로 많다.
주석을 남겨야겠다는 생각이 들면, 가장 먼저 주석이 필요 없는 코드로 리팩터링 해본다.
관련 리팩터링
함수 추출하기, 함수 선언 바꾸기, 어서션 추가하기