1. Mysterious Name, 기이한 이름
- 코드는 단순하고 명료하게 작성하는 것이 중요하다. 그 중 하나가 바로 이름.
- 이름이 명확하지 않다면 설계에 근본적인 문제가 있을 확률이 높고, 혼란스러운 이름을 잘 정리하다 보면 코드가 훨씬 간결해질 때가 많다.
2. Duplicated Code, 중복 코드
- 똑같은 코드 구조가 여러 곳에서 반복되면 하나로 통합해서 더 나은 방법으로 만들어야 한다.
3. Long Function, 긴 함수
- 함수를 짧게 구성하면 다음의 장점을 가진다.
- 관리하기 편리.
- 간접 호출의 효과.
- 코드를 이해하고 공유하기 쉬움.
- 선택하기 쉬움.
- 또한 예전과 달리 프로세스 안에서 함수 호출 비용이 거의 없음.
4. Long Parameter List, 긴 매개변수 목록
5. Global Data, 전역 데이터
- 전역데이터는 어디에서든 건드릴 수 있고, 값을 누가 바꿨는지 찾아낼 매커니즘이 없기 때문에 문제가 된다.
- 캡슐화를 하여 변화에 대처하기 쉽도록 하자.
- 캡슐화하는 모듈내에서만 데이터 가변 처리를 하도록 하면 변경되는 코드 추적에 용이.
6. Mutable Data, 가변 데이터
- 데이터를 변경했더니 예상치 못한 결과나 골치 아픈 버그가 발생하는 문제가 있다.
- 이 가변데이터의 유효범위가 커질수록 영향력도 커진다.
- 이를 해결하기 위해서 함수형 프로그래밍에서의 데이터는 절대 변하지 않고, 데이터를 변경하려면 반드시 변경하려는 값에 해당하느 복사본을 만들어서 반환한다는 개념을 가지고 있습니다.
- 다만 함수형 언어가 프로그래밍에서 차지하는 비중은 적고, 변수 값을 바꿀 수 있는 언어를 사용하는 프로그래머가 더 많습니다.
7. Divergent Change, 뒤엉킨 변경
- 일반적으로 뒤엉킨 변경은 단일 책임 원칙(SRP, Single Responsibility Principle)이 제대로 지켜지지 않을 때 발생한다.
- 하나의 모듈이 서로 다른 이유들로 인해 여러 가지 방식으로 변경되는 일이 많을 때 발생.
- 뒤엉킨 코드를 맥락별로 잘 분리하면 해결할 수 있다.
8. Shotgun Surgery, 산탄총 수술
- 뒤엉킨 변경과 비슷하면서도 정반대이다.
- 코드를 변경할 때마다 자잘하게 수정해야 하는 클래스가 많을 때 풍긴다.
- 이럴때는 함께 변경되는 대상들을 모두 한 모듈에 묶어두면 좋다.
9. Feature Envy, 기능 편애
- 어떤 함수가 자기가 속한 모듈의 함수나 데이터보다 다른 모듈의 함수나 데이터와 상호작용 할 일이 더 많을 때 풍긴다.
- 해결책은 해당 함수를 데이터의 근처로 옮겨주면 된다.
- 만약 함수가 사용하는 모듈이 다양하다면 함수를 추출하여 여러조각으로 나눈후 각각을 적합한 모듈로 옮기면 더 쉽게 해결할수도 있다.
10. Data Clumps, 데이터 뭉치
- 데이터 뭉치를 찾아서 하나의 객체로 묶게 되면 장점이 있다.
- 매개변수 객체 만들기나 객체 통째로 넘기기를 활용하면 되는데 이렇게 하면 메서드 호출 코드가 간결해 진다.
11. Primitive Obsession, 기본형 집착
- 대부분의 프로그래밍 언어는 다양한 기본형(정수, 보동소수점 수, 문자열 등)의 다양한 기본형을 제공한다.
- 다만 이를 오사용하면 좋지 않다. 필요한 타입이 기본형으로 구현하는 것 대신 직접 선언하여 사용하는게 좋다.
12. Repeated Switches, 반복되는 Swich문
- 중복되는 switch문이 문제가 되는 이유는 조건절을 하나 추가할 때마다 다른 모두 switch문도 모두 찾아서 함께 수정해야 하기 때문이다.
- 이러한 상황에서 다형성을 활용하면 코드베이스를 안정적으로 수정할 수 있다.
13. Loop, 반복문
- 반복문을 파이프라인으로 바꾸기를 적용하면 코드에서 각 원소들이 어떻게 처리되는지 쉽게 파악할 수 있다.
14. Lazy Element, 성의 없는 요소
- 본문 코드를 그대로 쓰는것과 크게 다를것이 없는 함수, 실질적으로 메서드가 하나뿐인 클래스와 같은 로직들은 잘판단하여 정리하는것이 좋다.
15. Speculative Generality, 추측성 일반화
나중에 필요한 것으로 당장은 필요없는 것은 로직을 말한다. 실제로 사용하지 않는다면 낭비일 뿐으로 걸리적 거리는 코드는 정리하는것이 좋다.
16. Temporary Field, 임시 필드
- 사용자가 쓰지 않는 것 처럼 보이는 필드가 존재하면 코드를 이해하기 어렵다.
- 이부분은 클래스로 추출후 함수 옮기기를 활용해 임시 필드들과 관련된 코드를 모조리 새 클래스에 몰아넣는다.
17. Message Chains, 메시지 체인
- 메세지 체인은 클라이언트가 한 객체를 통해 다른 객체를 얻은 뒤 방금 얻은 객체에 또 다른 객체를 요청하는 것을 말한다.
- 메시지 체인이 꼬리에 꼬리를 무는 경우가 있는데 이는 클라이언트가 객체 내비게이션 구조에 종속됐음을 의미한다. 그래서 네비게이션 중간 단계를 수정하면 클라이언트 코드도 수정해야한다.
- 위임 숨기기로 해결을 할 수 있는데 함수 추출하기로 결과 객체를 사용하는 코드 일부를 따로 빼낸 다음 함수 옮기기를 활용 하면 된다.
18. Middle man, 중개자
- 클래스가 제공하는 메서드 중 절반이 다른 클래스에 위임하고 있다면 중개자 제거하기를 활용하여 실제로 일을 하는 객체와 직접 소통하게 한다.
19. Insider Trading, 내부자 거래
- 모듈 사이의 데이터 거래가 많으면 결합도가 높아지기 때문에 좋지 않다.
- 따라서 데이터 거래의 양을 최소로 줄이고 투명하게 처리하도록 한다.
20. Large Class, 거대한 클래스
- 필드가 너무많은 클래스, 코드량이 너무 많은 클래스는 중복코드와 혼동을 일으킬 여지가 크다.
- 클래스 안에서 중복을 제거하고, 클래스를 쪼개는것도 해결책이 될 수 있다.
21. Alternative Classes with Different Interfaces, 서로 다른 인터페이스의 대안 클래스들
- 클래스 사용시 다른 클래스로 교체할 수 있다. 교체하려면 인터페이스가 같아야 하는데 메서드 시그니처를 일치시키고 인터페이스가 같아 질때까지 동작들을 클래스 안으로 밀어 넣는다.
- 그러다 대안 클래스들 사이에 중복 코드가 생기면 슈퍼클래스 추출하기를 적용할지 고려해본다.
22. Data Class, 데이터 클래스
- 데이터 클래스란 데이터 필드와 게터/세터 메서드로만 구성된 클래스를 말한다.
- 이런 클래스에 public 필드가 있다면 레코드를 캡슐화 하고, 변경하면 안되는 필드는 세터 제거하기로 접근을 원천 봉쇄한다.
- 다른 클래스에서 데이터 클래스의 게터나 세터를 사용하는 메서드를 찾아서 그 메서드를 데이터 클래스로 옮길 수 있으면 옮기도록 한다.
23. Refused Bequest, 상속 포기
- 부모 클래스의 메서드와 필드를 상속받고 싶지 않을 경우가 있다. 이는 계층구조를 잘 못 설계해서 그렇다.
- 해법은 같은 계층에 서브 클래스 하나를 새로 만들고 메서드, 필드를 새로 만든 서브 클래스로 넘긴다. 그러면 부모 클래스에는 공통된 부분만 남는다.
- 주석이 많으면 온갖 악취를 풍기는 코드가 나오기 쉽다. 특정 코드 블록이 하는 일에 주석을 남기고 싶다면 함수 추출하기를 적용해보고, 여전히 설명이 필요하다면 함수 이름을 바꾸어 본다.
- 주석을 남겨야겠다는 생각이 들면, 가장 먼저 주석이 필요 없는 코드로 변경해보자.
- 뭘 할지를 모를 때라면 주석을 달아두면 좋다.
- 확실하지 않은 부분에도 주석을 남겨놓으면 좋다.
- 코드를 지금처럼 작성해놓은 이유에 대해서 적어도 좋고 이는 유지보수에 도움이 될것이다.