3장, 코드에서 나는 악취

2021.09.27

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, 상속 포기

  • 부모 클래스의 메서드와 필드를 상속받고 싶지 않을 경우가 있다. 이는 계층구조를 잘 못 설계해서 그렇다.
  • 해법은 같은 계층에 서브 클래스 하나를 새로 만들고 메서드, 필드를 새로 만든 서브 클래스로 넘긴다. 그러면 부모 클래스에는 공통된 부분만 남는다.

24. Comments, 주석

  • 주석이 많으면 온갖 악취를 풍기는 코드가 나오기 쉽다. 특정 코드 블록이 하는 일에 주석을 남기고 싶다면 함수 추출하기를 적용해보고, 여전히 설명이 필요하다면 함수 이름을 바꾸어 본다.
  • 주석을 남겨야겠다는 생각이 들면, 가장 먼저 주석이 필요 없는 코드로 변경해보자.
  • 뭘 할지를 모를 때라면 주석을 달아두면 좋다.
  • 확실하지 않은 부분에도 주석을 남겨놓으면 좋다.
  • 코드를 지금처럼 작성해놓은 이유에 대해서 적어도 좋고 이는 유지보수에 도움이 될것이다.