1. 기이한 이름
- 함수, 모듈, 변수, 클래스 등은 그 이름만 보고도 각각이 무슨 일을 하고 어떻게 사용해야 하는지 명확하게 알 수 있도록 아주 신경써서 명명해야 한다.
2. 중복코드
- 코드가 중복되는 곳들의 차이점 주의깊게 살피기
- 완전 같을 때 : 함수 추출하기 (6.1절)
- 조금 다를 때 : 문장 슬라이스 하기 (8.6절) 로 비슷한 부분 한 곳에 모아 함수 추출을 더 쉽게 할 수 있는지 살펴보기
- 같은 부모로부터 파생된 서브클래스들에 코드가 중복되어있다면, 각자 따로 호출될 수 있도록 메서드 올리기 (12.1절) 적용해 부모클래스로 옮긴다.
3. 긴 함수
- 코드가 모든 기능을 끝없이 위임하는 방식으로 짧은 함수들로 이루어져 있는 것이 좋다.
- 이는 간접 호출의 효과를 갖는다. 즉 코드를 이해하고, 공유하고, 선택하기 쉬워진다는 장점은 함수를 짧게 구성할 때 나온다.
- 코드 읽는 사람은 함수가 하는 일을 파악하기 위해 왔다갔다 해야하지만, 이마저 이해하기 쉽게 만드는 방법은 함수 이름을 잘 짓는 것.
- 주석을 달아야 할 만한 부분은 무조건 함수로 만든다.
- 그 함수 본문에는 원래 주석으로 설명하려던 코드가 담긴다.
- 함수 이름은 동작방식이 아닌 의도가 드러나게 짓는다.
- 심지어 원래 코드보다 길어져도 함수로 뽑는다. 대신 함수 이름에 코드의 목적을 드러내야 한다.
- 여기서 핵심은 함수의 길이가 아니라, 함수의 목적(의도)와 구현 코드의 괴리가 얼마나 큰가이다.
- 무엇을 하는지를 코드가 잘 설명하지 못할수록 함수로 만드는 게 유리하다.
4. 긴 매개변수 목록
- 다른 매개변수에서 값을 얻어올 수 있는 매개변수가 있을 때 : 매개변수를 질의 함수로 바꾸기 (11.5절)
- 사용 중인 데이터 구조에서 값들을 뽑아 각각을 별개의 매개변수로 전달하고 있을 때 : 객체 통째로 넘기기 (11.4절)
- 항상 함께 전달되는 매개변수 : 매개변수 객체 만들기 (6.8절)
- 함수의 동작방식 정하는 플래그 역할의 매개변수 : 플래그 인수 제거하기 (11.3절)
- 특히 여러 개의 함수가 특정 매개변수들의 값을 공통으로 사용할 때 : 여러 함수를 클래스로 묶기 (6.9절) 로 공통값들을 클래스의 필드로 정의
5. 전역 데이터
- 전역 데이터는 코드베이스 어디에서든 건드릴 수 있고 값을 누가 바꿨는지 찾아낼 방법이 없다.
- 그래서 마치 유령 같은 원격 작용처럼 버그가 끊임없이 발생하는 원인
- 이를 방지하기 위해 변수 캡슐화하기 (6.6절) 를 사용한다.
- 다른 코드에서 오염시킬 가능성이 있는 데이터를 발견할 때마다 이 기법을 가장 먼저 적용
- 전역 데이터가 가변이라면 특히 다루기 까다롭다.
6. 가변 데이터
- 데이터를 변경했더니 예상치 못한 결과나 골치아픈 버그로 이어지는 경우
- 코드의 다른 곳에서는 다른 값을 기대할 수 있음
- 데이터를 변경하지 않고, 복사본을 만들어 반환하도록 하는 방법 사용
- 변수 캡슐화 하기 : 정해놓은 함수를 거쳐야만 값을 수정할 수 있도록 하면 값이 어떻게 수정되는지 감시하거나 코드를 개선하기 쉽다.
- 변수 쪼개기 : 용도별로 독립된 변수에 저장하도록 하고 하나의 값에 여러 용도로 쓰일 값 업데이트 X
- 문장 슬라이스하기, 함수 추출하기 : 갱신 로직은 다른 코드와 떨어뜨려 놓는 것이 좋다.
7. 뒤엉킨 변경
- 코드를 수정할 때는 시스템에서 고쳐야 할 딱 한 군데를 찾아서 그 부분만 수정할 수 없다면 뒤엉킨 변경과 산탄총 수술 중 하나가 풍긴다.
- 단일 책임 원칙(SRP)이 제대로 지켜지지 않을 때 나타난다.
- 예 : 서비스 하나가 추가될 때마다 또 다른 함수 4개를 바꿔야하는 모듈이 있다.
8. 산탄총 수술
- 코드를 변경할 때마다 자잘하게 수정해야 하는 클래스가 많을 때
- 변경할 부분이 코드 전반에 퍼져 있다면 찾기도 어렵고 꼭 수정해야 할 곳을 지나치기 쉽다.
- 맡았던 프로젝트 중에 서비스 하나가 추가될 때마다 곳곳에 서비스타입을 넣어줘야 하는 부분이 있었는데 한 번에 수정되지 않고 까먹고 넘어가는 부분이 생기곤 했다.
- 함수 옮기기(8.1절)와 필드 옮기기(8.2절)로 모두 한 모듈에 묶어두면 좋다.
9. 기능 편애
- 프로그램을 모듈화할 때는 코드를 여러 영역으로 나눈 뒤 영역 안에서 이뤄지는 상호작용은 최대한 늘리고 영역 사이에서 이뤄지는 상호작용은 최소로 줄여야한다.
- 기능 편애는 흔히 어떤 함수가 자기가 속한 모듈의 함수나 데이터보다 다른 모듈의 함수나 데이터와 상호작용할 일이 더 많을 때 풍기는 냄새다.
10. 데이터 뭉치
- 데이터 항목 서너 개가 여러 곳에서 항상 함께 뭉쳐 다니는 모습을 흔히 볼 수 있다.
- 관련 데이터를 하나의 클래스로 묶기
11. 기본형 집착
- 프로그래머 중에는 자신에게 주어진 문제에 딱 맞는 기초 타입(화폐, 좌표, 구간 등)을 직접 정의하기를 꺼리는 사람이 많다.
- 이 냄새는 문자열을 다루는 코드에서 특히 흔하다.
12. 반복되는 switch문
- 중복된 switch문이 문제가 되는 이유는 조건절을 하나 추가할 때마다 다른 switch문들도 모두 찾아서 함께 수정해야하기 때문
- 이럴 때 다형성을 활용해 정리
13. 반복문
- 지금은 일급 함수를 지원하는 언어가 많아졌기 때문에 반복문을 파이프라인으로 바꾸기를 적용해서 대체할 수 있다.
14. 성의없는 요소
15. 추측성 일반화
- '나중에 필요할거야'라는 생각으로 당장은 필요 없는 모든 종류의 후킹 포인트와 특이 케이스 처리 로직을 작성해둔 코드에서 풍긴다.
- 그 결과, 이해하거나 관리하기 어려운 코드가 발생
- 미래를 대비해 작성한 부분을 실제로 사용하게 되면 다행이지만, 그렇지 않다면 쓸데없는 낭비일 뿐이다.
- 죽은 코드 제거하기
16. 임시 필드
- 간혹 특정 상황에서만 값이 설정되는 필드를 가진 클래스도 있다.
- 하지만 객체를 가져올 때는 당연히 모든 필드가 채워져 있을거라 기대하는 게 보통이다.
- 이렇게 임시 필드를 갖도록 작성하면 코드를 이해하기 어렵다.
- 사용자는 쓰이지 않는 것처럼 보이는 필드가 존재하는 이유를 파악하느라 머리가 아프게 된다.
17. 메시지 체인
- 메시지 체인은 클라이언트가 한 객체를 통해 다른 객체를 얻은 뒤 방금 얻은 객체에 또 다른 객체를 요청하는 식으로, 다른 객체를 요청하는 작업이 연쇄적으로 이어지는 코드를 말한다.
- 위임 숨기기, 함수 옮기기, 함수 추출하기 등 사용하여 해결
18. 중개자
- 객체의 대표적인 기능 하나로, 외부로부터 세부사항을 숨겨주는 캡슐화가 있다.
- 캡슐화하는 과정에서 위임이 자주 활용된다.
- 하지만 이게 지나치면 문제가 된다.
- 예 : 클래스가 제공하는 메서드 중 절반이 다른 클래스에 구현을 위임
- 중개자 제거하기(7.8절)을 활용하여 실제로 일을 하는 객체와 직접 소통하도록 하기
19. 내부자 거래
20. 거대한 클래스
- 한 클래스가 너무 많은 일을 하려다 보면 필드 수가 상당히 늘어난다.
- 그리고 클래스에 필드가 너무 많으면 중복 코드가 생기기 쉽다.
- 클래스 추출하기(7.5절)로 필드를 일부 따로 묶거나, 슈퍼클래스 추출하기(12.8절)나 타입 코드를 서브클래스로 바꾸기(12.6절) 활용
- 중복 코드 제거
21. 서로 다른 인터페이스의 대안 클래스들
22. 데이터 클래스
23. 상속 포기
- 서브클래스는 부모클래스로부터 메서드와 데이터를 물려받는다.
- 하지만 원치 않는다면 필요한 몇개만 물려받아야한다.
- 먼저 같은 계층에 서브클래스를 하나 새로 만들고, 메서드 내리기(12.4절) 필드 내리기(12.5절)를 활용해서 물려받지 않을 부모 코드를 모조리 새로 만든 서브클래스로 넘긴다.
24. 주석
- 주석을 남기지 말라는 게 아니다. (휴~)
- 주석이 많으면 이 장에서 소개한 온갖 악취를 풍기는 코드가 나오기 쉽다.
- 주석을 남겨야겠다는 생각이 들면, 가장 먼저 주석이 필요없는 코드로 리팩터링해본다.
[마틴 파울러 - 리팩터링] 발췌
'[책 정리] 리팩터링' 카테고리의 다른 글
[월부] 재테크 기초반 2주차 수강 후기 (0) | 2024.11.25 |
---|---|
5장. 리팩터링 카탈로그 보는 법 (0) | 2024.01.06 |
4장. 테스트 구축하기 (0) | 2024.01.01 |
2장. 리팩터링 원칙 (0) | 2023.12.31 |
1장. 리팩터링 : 첫 번째 예시 (0) | 2023.12.31 |