Refactoring

[ 리팩토링 by 마틴 파울러 ] Chapter03. 코드에서 나는 악취

noahkim_ 2021. 7. 20. 22:50

저자는 리팩토링을 하는 시점을 코드에서 '악취'가 날 때라 말한다.

'악취'가 나는 코드를 어떤 정확한 기준에 부합한다 라고 보기는 어렵다.

하지만 그동안의 경험을 토대로 하여 가지게 된 직관을 정리하여 제시한 악취들을 공부한 후 리팩토링 작업을 하게될 때, 

감을 키워나가면서 리팩토링 실력을 쌓아 나가야 효과를 볼 수 있다.

 

3.1 기이한 이름

 

코드를 명료하게 표현하는데 이름만 보고도 각각이 무슨 일을 하고 어떻게 사용해야 하는지 알 수 있도록 신경써서 이름을 지어야 함. 

이름만 지어도 나중에 문맥을 파악하느라 해메는 시간을 크게 절약할 있음.

 

3.2 중복 코드

 

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

코드가 중복되면 서로 차이점이 없는 지 주의깊게 살펴봐야 하므로 중복 코드를 리팩토링 해주자

 

3.3 긴 함수

 

예전에는 서브루틴을 호출하는 비용이 컸기 때문에 짧은 함수를 꺼렸다. 

하지만 요즘 언어는 프로세스 안에서의 함수 호출 비용을 거의 없애버렸다. 

짧은 함수로 구성된 코드를 이해하기 위해 함수 이름을 짓고 호출 비용이 거의 없다는 장점을 이용하기 위해 함수를 적극적으로 쪼갠다.

 

3.4 긴 매개변수 목록

 

매개 변수 목록이 길어지면 그 자체로 이해하기가 어려움. 

 -> 클래스를 이용하여 매개변수 목록을 줄이기.

 

특히 여러 개의 함수가 특정 매개변수들의 값을 공통으로 사용할 때 유용함. 

공통 값들을 클래스의 필드로 정의하여 처리하면 훨씬 효과적

 

3.5 전역 데이터

 

전역데이터는 악취 중 가장 지독한 축에 속함.

코드 베이스 어디서든 건드릴 수 있고 값을 누가 바꿨는지 찾아낼 매커니즘이 없음.

대표적인 형태는 전역 변수지만 클래스 변수와 싱글톤에서도 같은 문제가 발생함.

 

이를 방지하기 위해 ‘변수 캡슐화하기’ 리팩터링을 진행한다

함수로 이러한 데이터를 감싸는 것만으로도 데이터를 수정하는 부분을 수비게 찾을 수 있고 접근을 통제할 수 있음.

 

또한 전역 데이터가 가변이라면 특히나 다루기 까다롭다. 

 

3.6 가변 데이터

 

데이터를 변경했더니 예상치 못한 결과나 골치 아픈 버그로 이어지는 경우가 종종 있다.

또한 원인을 알아내기가 매우 어렵다. 

이런 이유로 함수형 프로그래밍에서는 데이터는 절대 변하지 않고, 데이터를 변경하려면 반드시

변경하려는 값에 복사본을 만들어서 반환한다는 개념을 기본으로 삼고 있다. 

 

3.7 뒤엉킨 변경

 

단일 책임 원칙(SRP. Single Responsibility Principle)이 제대로 지켜지지 않을 떄 나타난다.

( 단일 책임 원칙은 단일 모듈은 변경의 이유가 하나, 오직 하나여야 하며, 그 이유는 하나의 액터에 대해서만 책임지는 원칙이 어긋날 경우이다 )

 

즉, 하나의 모듈이 서로 다른 이유들로 인해 여러 가지 방식으로 변경되는 일이 많을 때 발생.

이를 해결하기 위해 서로다른 맥락에서 이루어지는 동작들은 독립된 모듈로 분리하여 처리하기

 

3.8 산탄총 수술

 

뒤엉킨 변경과 비슷하면서도 정반대.

 

같은 맥락의 코드가 모여있지 않고 퍼져있음.

 

이러한 경우에는 코드를 변경할 때마다 자잘하게 수정해야 하는 클래스가 많을 때 풍김. 

변경할 부분이 코드 전판에 퍼져 있다면 찾기도 어렵고 꼭 수정해야 할 곳을 지나치기 어려움. 

 

해법은 함수나 필드를 한 모듈에 묶어두면 된다. 

 

3.9 기능 편애

 

어떤 함수가 자기가 속한 모듈의 함수나 데이터보다 다른 모듈의 함수나 데이터와 상호작용할 일이 더 많을 때 풍기는 냄새.

 

이러한 경우 상호작용을 자주하는 곳에 옮겨주기

 

3.10 데이터 뭉치

 

함수 호출 혹은 어떤 로직에 같이 쓰이는 데이터 항목들을 뭉쳐서 새로운 보금자리에 두어 사용하기.

 

3.11 기본형 집착

 

자신의 주어진 문제에 딱 맞는 기초 타입(화폐, 좌표, 구간 등)을 직접 정의하기.

 

범위처리를 조건문을 사용하여 처리한다던지 전화번호를 단순히 문자 집합으로만 표현하는 코드가 대표적인 악취임.

 

3.12 반복되는 switch문

 

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

 

3.13 반복문

 

지금은 일급 함수를 지원하는 언어가 많아졌기 때문에 반복문을 파이프라인으로 바꾸기를 적용해서 반복문 사용을 줄여 성능 높이기

 

3.14 성의 없는 요소

 

구조적으로 의미가 있어 정의된 클래스나 함수가 막상 사용되지 않는 경우, 제거하기

 

3.15 추측성 일반화

 

‘나중에 필요할 거야’ 라는 생각으로 당장 필요없는 후킹 포인트와 특이 케이스 처리 로직을 작성해둔 코드.

 

걸리적거리는 코드는 테스트코드 지우고 함수도 지우기

 

3.16 임시 필드

 

간혹 특정 상황에서만 값이 설정되는 필드들을 클래스가 가지게 되면 코드를 이해하기 어려움.

이럴 경우 클래스를 따로 추출하여 임시 필드와 관련된 코드를 모조리 옮겨준다. 

 

3.17 메시지 체인

 

메시지 체인은 클라이언트가 한 객체를 통해 다른 객체를 얻은 뒤 방금 얻은 객체에 또 다른 객체를 요청하는 식으로, 다른 객체를 요청하는 작업이 연쇄적으로 이어지는 코드를 말한다. 이는 클라이언트가 객체 내비게이션 구조에 종속됐음을 의미한다. 

 

이럴 경우 위임 숨기기로 해결한다. 

그리고 최종 결과 객체가 어떻게 쓰이는 살펴볼 있도록 코드를 따로 빼놓고 함수 옮기기로 체인을 숨긴다. 

 

3.18 중개자

 

캡슐화 시, 위임을 활용하는 경우가 많다. 하지만 한쪽에 쏠릴 정도의 위임을 하게되면 의도파악도 어렵고 책임 분배도 적절하지 않다.

이러한 경우 실제로 일을 하는 객체와 직접 소통하게 하면 된다.

 

3.19 내부자 거래

 

모듈간의 결합도가 높으면 서로에게 영향을 주게되어 시스템적으로 손해임.

모듈간 거래를 하려면 양을 최소로 줄이고 모두 투명하게 처리함. 

 

3.20 거대한 클래스

 

한 클래스가 너무 많은 일을 하게되면 필드 수가 상당히 늘어난다. 

그럴 경우 중복 코드가 생기기 쉽다. 

 

이러한 경우 클래스를 쪼개는 등의 리팩토링 활용하여 클래스를 분리함

 

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

 

클래스의 큰 장점은 필요에 따라 언제든 다른 클래스로 교체 가능.

, 인터페이스가 같아야 . 

 

3.22 데이터 클래스 

 

데이터 클래스란 데이터 필드, 게터, 세터로만 이루어진 클래스를 말한다. 

데이터 저장용이므로 다른 클래스가 함부로 깊게 다룰 수 있음. 

 

이러한 경우 캡슐화하기와 세터를 제거함으로 해결한다

 

3.23 상속 포기

 

서브클래스가 부모의 유산을 원치 않는 경우 혹은 부분적으로만 원할 경우, 상속 매커니즘을 벗어나기

 

3.24 주석

 

보통 주석이 장황하게 달린 원인이 코드를 잘못 작성했을 경우가 있음.  

이러한 경우 확정된 부분만 간단히 주석을 달고 진행중이라는 주석을 달아 계속 정리해나가기