Ch 3. 코드에서 나는 악취
리팩터링을 언제 시작해야 할 지에 관한 내용
바로 코드에서 '악취'가 나는 경우. 언제 끝내는지는 본이느이 직관이 필요하고, 이 장에서는 징후에 대해서 소개한다. 그 징후는 다음과 같다.
-
기이한 이름
- 마땅한 이름이 떠오르지 않는다면 설계에 근본적인 문제가 숨어있을 수 있다.
-
중복 코드
- 함수 추출하기, 문장 슬라이드, 메서드 올리기 기법
-
긴 함수
- 간접 호출의 효과를 생각. 좋은 라이브러리나 소프트웨어의 코드를 보면 짧은 함수들이 끝없이 위임함.
- 함수를 적극적으로 쪼개자. 주석 달아야 하면 빼서 이름으로 표현하자.
- 핵심은 함수의 길이가 아닌, '무엇을 하는지'를 코드가 설명할 수 있어야 함.
-
긴 매개 변수 목록
-
전역 데이터
-가장 무서운놈. 변수 캡슐화하기 적용이 필요함.
-함수로 감싸서 접근을 제어. 모듈이나 클래스에 넣어서 사용을 제한하는 등 가능
-
가변 데이터
- 데이터를 변경했다가 조져지는 경우..
- 변수 캡슐화 하기, 변수 쪼개기, 문장슬라이드-함수추출하기 등. 세터 제거하기도 여기서 나오는구나.
-
뒤엉킨 변경
- SRP 원칙을 잘 지키지 않았을 때 나타나는 현상.
- 즉, 하나의 모듈이 여러가지 다른 이유로 여러 번 변경되어야 하는 경우
- 가령, 데이터베이스 추가될 때 함수 세개가 바뀌고, 금융 상품이 추가될 때 함수 네개가 바뀌어야 하는 경우.
-
산탄총 수술
- 코드를 변경 할 때마다 자잘자잘하게 여기저기 수정해야 하는 클래스가 많을 때.
- 뒤엉킨 변경과 다르게, 맥락별로 모아서 해결.
-
기능 편애
- 어떤 함수가 자기가 속해있는 모듈의 함수나 데이터보다 외부를 더 많이 쓸 때.
- 그 함수가 원하는 곳으로 보내버리자.
- 만약 함수가 여러 외부 데이터, 함수를 사용하고 있다면, 가장 많이 쓰는 곳으로 보내주던가 혹은 잘개 쪼개서 각각 보내버리자.
- 전략 패턴, 방문자 패턴, 자기 위임 등. 함께 변경할 대상을 한데 모으는 것
-
데이터 뭉치
- 데이터들이 어린애들 마냥 막 같이 어울려 다니는 애들이 있음. ㄱㅇㅇ..
- 이런 애들은 클래스 추출하기로 묶어버리던가, 매개변수 객체 만들기, 객체 통째로 넘기기를 적용, 매개변수 수를 줄여보자
-
기본형 집착
-
반복되는 switch문
- 굉장히 사악하다고 하기는 하는데.. 그건 다형성이 널리 쓰이지 않았을 때라 ㅎ 지금은 똑같은 조건부 로직 (switch, if/esle 길게 있는)이 여러 곳에서 반복해 등장하는 코드를 살펴보자.
-
반복문
- filter, map 과 같이 반복문을 파이프라인으로 바꾸기를 적용해서 반복문을 제거할 수 있게됨.
- 일급함수를 지원하는 언어가 많으니까..
-
성의 없는 요소
- 빈약한 함수나 클래스.. 구조가 필요 없는 애들은 보내주기.
-
추측성 일반화
- '나중에 필요할 거야..'라는 심정으로 작성한, 모든 종류의 후킹 포인트, 케이스 처리 로직을 작성한 코드.
- YAGNI 와 같은 맥락인것 같음.
-
임시 필드
- 특정 상황에서만 값이 설정되는 필드를 가진 클래스들. 보통은 객체를 가져올 때는 거기에 값이 있을거라 생각하니까... 빼주자. 클래스 추출하기 후 함수 옮기기로 새 클레스에 몰아 넣어주자.
-
메시지 체인
- 게터로 계속 가져오거나.. 객체를 계속 참조해서 가져오는 경우. 굉장히 많은 것 같음 이런 경우는.
- 옮긴이의 주석을 읽어보면 이해가 감.
- 방법은 두가지
* 객체에 위임 숨기기 : 부서장의 이름을 바로 반환하도록 person이나 department 메서드를 추가.
- 만약 다수의 클라이언트가 이를 사용한다면, 메서드를 적당한 모듈로 빼버리고 체인을 숨겨버림.
-
중개자
- 클래스가 제공하는 메서드 중 절반이 다른 클래스에게 구현을 위임한다면..? 노답... 중개자 제거하기, 함수 인라인하기로 조지기.
-
내부자 거래
- 모듈끼리 커플링이 높은걸 모두 그켬함.
- 근데 커피 자판기 옆에서 은밀히 데이터를 주고받는 모듈들이 있다면, 함수 옮기기와 필드 옮기기로 서로 떼어놓아 조져주기.
- 혹은 공통 부분을 처리하는 새로운 모듈, 위임 숨기기를 사용해서 중간자 역할을 하게.
- 상속의 경우, 서브클래스를 위임으로 바꾸기, 슈퍼클래스를 위임으로 바꾸기로 조져주자.
-
거대한 클래스
- 한 클래스가 너무 많은 일을 하려다 보면 필드가 상당히 늘어남.
- 일단 클래스 추출하기로 필드들 일부를 따로 묶자.
- 분리할 친구들을 상속 (슈퍼클래스 추출, 서브 클래스 추출) 등으로 조지기.
- 사실 클래스 내에서 중복을 제거하는게 제일 간단하긴 함.
- 클라이언트들이 거대 클래스를 이용하는지 패턴을 파악해서 이걸 단서로 쓸 수도 있음. 특정 그룹만 사용된다면, 얘네는 조져질 후보들
-
서로 다른 인터페이스의 대안 클래스들
- 클래스는 언제든 교체 가능한게 장점인데 그러려면 인터페이스가 같아야지.
- 함수 선언 바꾸기로 메서드 시그니처를 일치시키고, 함수 옮기기 등으로 인터페이스 같아질 때까지 필요한 동장을 밀어넣음
- 대안 클래스 사이에 슈퍼클래스 추추하기도 고려.
-
데이터 클래스
- 데이터랑 getter/setter만 있는. dto 이런 느낌.
- 그러다보니 다른 애들이 막 너무 깊게 함부로 막 다뤄버림..
- public은 레코드 캡슐화하기로 숨겨버리고, 세터는 앵간하면 제거해버리자.
- 클라이언트 코드가 데이터 클래스로 옮겨야 되는 경우도 있고..
- 불변 필드는 굳이 getter로 안하고 public 공개 해도 되기도..
-
상속 포기
- 부모 클래스의 유산 중에서 몇개만 관심 있고 나머지는 필요 없는 경우는..?
- 서브 클래스를 새로 만들어서 거기다가 싹 내려버리고, 부모는 공통으로 유지해버리기.
- 굳이 부모 클래스가 꼭 추상클래스여야만 하는 이유는 없음. 기능을 재활용하려고 만들기도 하니까. 악취가 심한 것도 아니고.
- 부모의 동작은 필요한데 인터페이스는 안 따르면 그건 좀 선넘음. 서브클래스를 위임으로 바꾸거나 슈퍼클래스를 위임으로 바꿔서 상속 메커니즘에서 벗어나버리기.
-
주석
- 주석 달아도 됨 ㅋㅋ
- 일단 최대한 주석이 필요 없는 코드로 리팩터링 하는게 필요할 뿐.
Ch4. 테스트 구축하기
TDD에서 워낙 상세하게 다루는 내용이라.. 굳이 기록하고 싶지는 않다. 눈여겨볼 만한 내용 위주로
모든 테스트를 완전히 자동화하고 그 결과까지 스스로 검사하게 만들자
요새는 워낙 테스트 프레임워크가 많으니까.. 잘 활용하면 될 것 같다.
- 회귀버그 : 잘 작동하던 기능에서 문제가 생기는 현상, 프로그램 변경중 뜻하지 않게 생긴 버그. 기존에 잘 작동하던 기능이 검사하는걸 회귀 테스트라곻 함.
이 회귀 버그 잡는 데에도 좋다.
TDD
코드를 작성하기 전 부터 테스트 코드부터 조져버리는데, 만약 리팩터링 대상의 test suite를 작성한다면 취약한 부분을 우선적으로 작성하자.
실패해야 할 상황에서는 반드시 실패하게 만들자
전부 통과한다..? 그러면 이제 테스트 프레임워크가 또 안믿기거든. 실패하는 것 까지 확인하자.
기댓값 230을 넣는 과정
저자는 검증과정에서 expect(asia.profit).eqaul(230) 코드를 작성할 때, 처음에는 임의의 값을 넣고 시스템이 뱉는 값을 확인한 후 이 값으로 바꾸고, 이후 계산 로직에 에러를 심어서 오류를 걸러내는걸 확인한다.
즉, 임시 값 설정 -> 실제 값 대체 -> 오류를 심었다가 되돌리기
처음의 코드를 믿는건데.. 그게 맞나 싶다.
beforeEach
당연히 테스트코드에서도 중복은 피해야 하는 거고, 그러다 보면 테스트 코드들이 공유하는 대상이 생길 수도 있는데..이는 바람직하지 못하다. 순서에 영향을 받을 수도 있으니까. 독립적으로 초기화해서 사용할 수 있도록 하자. 물론 불변이면 공유해도 되고. 크게 느려지는 일도 없다고 한다.
Ch5. 리팩터링 카탈로그
그렇다.