본문 바로가기
핌팩토링

3. 코드에서 나는 악취

by AsCE_hyunseung 2022. 2. 28.

3.코드에서 나는 악취

  • 리팩터링을 언제 시작하고 언제 그만할지를 판단하는 일은 리팩터링 작동 원리를 아는 것 못지않게 중요하다
  • 종료 기준보다는 리팩터링하면 해결할 수 있는 문제의 징후를 제시하겠다.

3.1 기이한 이름

  • 코드는 단순하고 명료하게 작성해야 한다.
  • 이름만 잘 지어도 나중에 문맥을 파악하느라 헤매는 시간을 크게 절약할 수 있다.

3.2 중복 코드

  • 똑같은 코드 구조가 여러 곳에서 반복된다면 하나로 통합하여 더 나은 프로그램을 만들 수 있다.

3.3 긴 함수

  • 오랜 기간 잘 활용되는 프로그램들은 짧은 함수로 구성됐다.
  • 간접 호출, 코드를 이해하고, 공유하고, 선택하기 쉬워진다는 장점은 함수를 짧게 구성할 때 나오는 것이다.
  • 그러기 위해서는 훨씬 적극적으로 함수를 쪼개야한다.

3.4 긴 매개변수 목록

  • 매개변수 목록이 길어지면 그 자체로 이해하기 어려울 때가 많았다.
  • 클래스는 매개변수 목록을 줄이는 데 효과적인 수단이기도 하다.
  • 특히 여러 개의 함수가 특정 매개변수들의 값을 공통으로 사용할 때 유용하다.

3.5 전역 데이터

  • 전역 데이터는 코드베이스 어디에서든 건드릴 수 있고 값을 누가 바꿨는지 찾아낼 메커니즘이 없다는 게 문제다.
  • 다른 코드에서 오염시킬 가능성이 있는 데이터를 함수를 감싸는 것만으로도 데이터를 수정하는 부분을 쉽게 찾을 수 있고 접근을 통제할 수 있게 된다.
  • 전역 데이터가 아주 조금만 있더라도 캡슐화를 해서 소프트웨어가 진화하는 데 따른 변화에 대처할 수 있다.

3.6 가변 데이터

  • 코드의 다른 곳에서는 다른 값을 기대한다는 사실을 인식하지 못한 채 수정해버리면 프로그램이 오작동한다.
  • 정해놓은 함수를 거쳐야만 값을 수정할 수 있도록 하면 값이 어떻게 수정되는지 감시하거나 코드를 개선하기 쉽다. (변수 캡슐화하기)
  • 갱신 로직은 다른 코드와 떨어뜨려 놓는 것이 좋다.(문장 슬라이드, 함수 추출하기)
  • 변수를 갱신하는 코드들의 유효범위를 제한해야한다.

3.7 뒤엉킨 변경

  • 뒤엉킨 변경은 SRP가 지켜지지 않을 때 나타난다.
  • 단계 쪼개기, 함수 옮기기, 함수 추출하기, 클래스 추출하기

3.8 산탄총 수술

  • 코드를 변경할 때마다 자잘하게 수정해야 하는 클래스가 많을 때 풍긴다.
  • 변경할 부분이 코드 전반에 퍼져 있다면 찾기도 어렵고 꼭 수정해야 할 곳을 지나치기 쉽다.
  • 함수 옮기기, 필드 옮기기, 여러 함수를 클래스로 묶기, 여러 함수를 변환 함수로 묶기, 단계 쪼개기, 함수 인라인하기, 클래스 인라인하기

3.9 기능 편애

  • 어떤 함수가 자기가 속한 모듈의 함수나 데이터보다 다른 모듈의 함수나 데이터와 상호작용 할 일이 더 많을때 풍기는 냄새이다.
  • 이 함수가 데이터과 가까이 있고 싶어 한다는 의중이 뚜렷이 드러나므로 데이터 근처로 옮겨주면 된다.
  • 어디로 옮길지가 불분명하다면, 가장 많은 데이터를 포함한 모듈로 옮긴다.
  • 데이터와 이를 활용하는 동작은 함께 변경해야 할 때가 많지만 예외가 있다. 그럴 때는 같은 데이터를 다루는 코드를 한 곳에서 변경할 수 있도록 옮긴다.

3.10 데이터 뭉치

  • 몰려다니는 데이터 뭉치는 보금자리를 따로 마련해야한다.
  • 클래스 추출하기, 매개변수 객체 만들기, 객체 통째로 넘기기
  • 간단한 레코드 구조가 아닌 클래스를 이용하자. 클래스를 이용하면 좋은 향기를 흩뿌릴 기회가 생긴다.

데이터 뭉치인지 판단하는 방법

  • 값 하나를 삭제했을때 나머지 데이터만으로는 의미가 없을때

3.11 기본형 집착

  • 자신에게 주어진 문제에 딱 맞는 기초 타입을 직접 정의하자.
  • 자료형들을 문자열로만 표현하지 말자.
  • 기본형을 객체로 바꾸기, 타입 코드를 서브 클래스로 바꾸기, 조건부 로직을 다형성으로 바꾸기

3.12 반복되는 switch문

  • 중복된 switch문이 문제가 되는 이유는 조건절을 하나 추가할 때마다 다른 switch 문들도 모두 찾아서 함께 수정해야 하기 때문이다.
  • 이럴때 다형성을 이용하자

3.13 반복문

  • 반복문을 파이프라인으로 바꾸기를 적용해서 시대에 걸맞지 않은 반복문을 제거할 수 있게 됐다.

3.14 성의 없는 요소

  • 본문 코드를 그대로 쓰는 것과 진배없는 함수, 실질적으로 메서드가 하나뿐인 클래스
  • 사정이 어떠하든 이런 프로그램 요소는 고이 보내드리는 게 좋다.

3.15 추측성 일반화

  • 당장은 필요없는 모든 종류의 후킹 포인트와 특이 케이스 처리 로직을 작성해둔 코드에서 풍긴다.
  • 이 냄새는 테스트 코드 말고는 사용하는 곳이 없는 함수나 클래스에서 흔히 볼 수 있다.

3.16 임시 필드

  • 특정 상황에서만 값이 설정되는 필드를 가진 클래스에서 객체를 가져올때.
  • 하지만 객체를 가져올 때는 당연히 모든 필드가 채워져 있으리라 기대한다.
  • 임시 필드를 갖도록 작성하면 코드를 이해하기 힘들다.

3.17 메시지 체인

  • 다른 객체를 요청하는 작업이 연쇄적으로 이어지는 코드
  • 이는 클라이언트가 객체 네비게이션 구조에 종속됐음을 의미한다.
managerName = aPerson.department.manager.name // 메시지 체인의 예

managerName = aPerson.managerName // department, manager 객체 존재를 숨김

3.18 중개자

  • 클래스가 제공하는 메서드 중 절반이 다른 클래스에 구현을 위임하고 있다면 이것은 문제가 된다.
  • 중개자 제거하기, 함수 인라인하기

3.19 내부자 거래

  • 일이 돌아가게 하려면 거래가 이뤄질 수 밖에 없지만, 그 양을 최소로 줄이고 모두 투명하게 처리해야한다.
  • 여러 모듈이 같은 관심사를 공유한다면 공통 부분을 정식으로 처리하는 제3의 모듈을 새로 만들거나 위임 숨기기를 이용하여 다른 모듈이 중간자 역할을 하게 만든다.

3.20 거대한 클래스

  • 한 클래스가 너무 많은 일을 하려다 보면 필드 수가 상당히 늘어난다.
  • 그리고 클래스에 필드가 너무 많으면 중복 코드가 생기기 쉽다.
  • 필드가 너무 많은 클래스와 마찬가지로 코드량이 너무 많은 클래스도 중복 코드와 혼동을 일으킬 여지가 크다.
    • 가장 간단한 해법은 그 클래스 안에서 자체적으로 중복을 제거하는 것이다.
  • 클라이언트들이 거대 클래스를 이용하는지 패턴을 파악하여 그 클래스를 어떻게 쪼갤지 단서를 얻을 수도 있다.

3.21 서로 다른 인터페이스의 대안 클래스들

  • 클래스를 사용할 때의 큰 장점은 필요에 따라 언제든 다른 클래스로 교체할 수 있다는 것이다.
  • 단 교체하려면 인터페이스가 같아야한다.
  • 함수 선언 바꾸기로 메서드 시그니처를 일치시킨다.
  • 함수 옮기기를 이용하여 인터페이스가 같아질 때까지 필요한 동작들을 클래스 안으로 밀어 넣는다.

3.22 데이터 클래스

  • 데이터 클래스란 데이터 필드와 게터, 세터 메서드로만 구성된 클래스를 말한다.
  • 데이터 저장 용도로만 쓰이다 보니 다른 클래스가 너무 깊이까지 함부로 다룰 때가 많다.
    • 레코드 캡슐화하기, 세터 제거하기
  • 데이터 클래스는 필요한 동작이 엉뚱한 곳에 정의돼 있다는 신호일 수 있다
    • 클라이언트 코드를 데이터 클래스로 옮기기만 해도 개선된다.

3.23 상속 포기

  • 부모의 수많은 유산 중에서 관심 있는 몇 개만 받고 끝내려는 경우도 있다. 예전에는 계층 구조를 잘못 설계했기 때문으로 봤다.
  • 일부 동작을 재활용하기 위한 목적으로 상속을 활용하기도 하는데, 실무 관점에서 유용한 방식이다
  • 서브클래스가 부모의 동작을 필요로하지만 인터페이스는 따르고 싶지 않을때 이 냄새가 난다

3.24 주석

  • 주석이 장황하게 달린 원인이 코드를 잘못 작성했기 때문인 경우가 많다.
  • 주석을 남겨야겠다는 생각이 들면, 가장 먼저 주석이 필요 없는 코드로 리팩터링해본다.

'핌팩토링' 카테고리의 다른 글

7. 캡슐화  (0) 2022.04.27
6. 기본적인 리팩터링  (0) 2022.03.26
4. 테스트 구축하기  (0) 2022.03.12
2. 리팩터링 원칙  (0) 2022.02.13
1. 리팩터링 첫번째 예시  (0) 2022.02.02