“소프트웨어 엔지니어링의 목적은 복잡성을 만드는 것이 아니라 제어하는 것입니다.”
패멀라 제이브Pamela Zave, 존 벤틀리Jon Bentley
『생각하는 프로그래밍』(인사이트, 2013)
블로터 bloater 는 코드가 커지고 많은 사람과 협업할 때 피할 수 없는 현상입니다. 블로터는 성능 문제를 일으키는 경우는 적지만 유지 관리 및 테스트 기능을 손상시켜 소프트웨어가 발전하는데 방해가 됩니다. 불필요한 기능이 포함되거나 잘못된 설계 결정 또는 과도한 반복으로 인해 코드가 불필요하게 커지고 복잡해져서 유지 관리하기가 어려워지는 경우가 많습니다.
긴 메서드를 작성하지 않고 작은 부분만 추가하고 팀원이 더 추가하는 식으로 하다 보면 코드가 조금씩 부풀어 오르다가 어느새 엉망진창이 된 코드를 발견하게 됩니다. 일종의 기술 부채라고 할 수 있죠. 이럴 땐 클린 코드의 법칙에 맞춰 ‘함수’를 작은 조각으로 추출하거나 과도한 인수를 줄이거나 선택적으로 삭제하는 등의 활동으로 블로터를 방지하고 해결할 수 있겠습니다. 함수를 작고 간결하게 하기 위한 비법을 몇 가지 레시피로 통해 알아보겠습니다.
[문제] 코드 길이가 너무 긴 메서드가 있습니다.
[해결] 긴 메서드를 작은 조각으로 추출하세요. 복잡한 알고리듬을 여러 부분으로 나누세요. 이러한 부분을 단위 테스트할 수 있습니다.
[설명] 긴 메서드는 응집력이 낮고 결합력이 높습니다. 즉, 디버깅하기 어렵고 재사용성도 낮죠. 이 레시피를 사용하면 구조화된 라이브러리와 도우미를 더 작은 동작으로 나눌 수 있습니 다. 프로그래밍 언어에 따라 적당한 줄의 수는 다르지만, 대부분 8~10줄이면 충분합니다. 다음은 긴 메서드입니다.
다음과 같이 여러 부분으로 나눌 수 있습니다.
이제 각 메서드를 단위 테스트할 수 있지만, 테스트를 구현 세부 사항과 연결하지 않도록 주의 해야 합니다. 모든 린터는 메서드가 미리 정의된 임곗값보다 클 때 이를 측정하고 경고할 수 있습니다.
[함께 보기] 리팩터링 구루의 ‘Long Method’
[문제] 인수가 너무 많은 메서드가 있습니다.
[해결] 메서드에 인수를 세 개 이상 전달하지 마세요. 관련 인수를 매개변수 객체로 함께 그룹화하세요. 이들을 함께 묶을 수 있습니다.
[설명] 인수가 너무 많은 메서드는 유지 보수성과 재사용률이 낮고, 결합성이 높습니다. 인수를 그룹화해 인수 간의 일관된 관계를 찾거나 인수의 컨텍스트가 포함된 작은 객체를 만들어야 합니다. 이러한 컨텍스트를 만들면 빠르게 실패하기 원칙에 따라, 매개변수 생성 시 이들 간의 관계를 적용합니다.
또한 문자열, 배열, 정수 등과 같은 ‘기본’ 타입은 피하고, 작은 객체를 고려해야 합니다. 인수를 연관시키고 그룹화해야 합니다. 항상 현실 세계의 매핑을 선호하세요. 현실 세계의 인수가 어떻게 응집력 있는 객체로 그룹화되는지 파악하세요. 함수가 너무 많은 인수를 받는다면 그중 일부는 클래스 구성과 관련이 있을 수 있습니다.
다음 예제는 여러 인수를 가진 print 메서드를 호출합니다.
대신, 기본형 집착을 피하기 위해 몇 가지 인수를 그룹화하세요.
대부분의 린터는 인수 목록이 너무 크면 경고를 표시하므로 필요한 경우 이 레시피를 적용할수 있습니다.
[문제] 긴 알고리듬이 포함된 메서드가 있습니다. 알고리듬을 이해하고, 테스트하고, 일부를 재사용하고 싶습니다.
[해결] 알고리듬을 객체 내부로 옮기고 작은 부분으로 나눕니다.
[설명] 긴 메서드는 디버깅하고 테스트하기 어렵습니다. 특히 그 메서드들이 protected 가시성을 가지고 있다면 더욱 그렇습니다. 알고리듬은 현실 세계에 존재하며 고유한 객체로 표현될 자격이 있습니다. 메서드 호출을 나타내는 객체를 생성하고, 큰 메서드를 새 객체로 이동하고, 메서드의 임시 변수를 private 속성으로 변환해야 합니다. 마지막으로 메서드 호출에서 매개 변수를 private 속성으로 변환해 제거합니다.
메서드 객체는 메서드 추출 extract method 을 여러 번 실행할 때 그중 일부 상태를 알고리듬의 일부로 전달할 때 적합합니다. 메서드 객체를 사용할 수 있는 강력한 지표는 계산이 호스트 메서드와 밀접하게 관련되어 있지 않을 때입니다. 익명 함수를 보다 원자적 atomic 이고 응집력 있으며 테스트 가능한 메서드 객체로 재정의할 수도 있습니다.
잔액을 계산하는 매우 긴 메서드를 상상해보세요.
이를 구체화하고 리팩터링하면 다음과 같습니다.
일부 IDE에는 함수를 메서드 객체로 추출하는 도구가 있습니다. 안전한 방식으로 자동으로 변경하고 로직을 새 컴포넌트로 추출해 단위 테스트, 재사용, 교환 등의 작업을 수행하세요..
[함께 보기]
• 켄트 벡의 저서 『Smalltalk Best Practice Patterns』(피어슨, 1996)의 3장에서 메서드 객체를 정의하는 부분
• C2 Wiki의 ‘메서드 객체’
[문제] 클래스에 메서드가 너무 많이 있습니다.
[해결] 클래스를 보다 응집력 있는 작은 조각으로 나누세요. 그리고 클래스에 우발적인 프로토 콜을 추가하지 마세요.
[설명] 엔지니어는 프로토콜을 가장 적합하다고 생각되는 첫 번째 클래스에 넣는 경향이 있습 니다. 테스트에서 기능을 커버한 후에 리팩터링하면 되니 문제가 되지는 않습니다. 다음 예제의 도우미 클래스에는 일관성이 없는 메서드가 많습니다.
MAPPER 개념을 사용해 관련 추상화 묶음으로 나눕니다.
대부분의 린터는 메서드 숫자를 세고 관련 클래스를 리팩터링할 수 있도록 경고합니다. 작고 재사용 가능한 객체로 분할하는 것은 좋은 습관입니다.
[함께 보기] 리팩터링 구루의 ‘Large Class’
[문제] 함수에 선택적 인수가 있습니다.
[해결] 선택적 인수는 더 간결한 코드의 이름 역할을 하면서 숨겨진 결합을 생성합니다.
[설명] 선택적 인수가 있는 경우, 호출 메서드가 우발적인 선택적 값에 결합되어 예기치 않은 결과와 부작용, 파급 효과를 생성할 수 있습니다. 선택적 인수가 있지만 기본 유형으로 제한된 언어에서는 플래그를 설정하고 우발적인 if 문을 추가해야 합니다. 이에 대한 해결책으로 인수를 명시적으로 만들고 언어에서 지원하는 경우 명명된 매개변수를 사용해야 합니다.
다음은 선택적 유효성 검사 정책이 있는 예제입니다.
인수를 명시적으로 만들면 숨겨진 가정이 없게 됩니다.
언어가 선택적 인수를 지원하면 이를 쉽게 감지할 수 있습니다. 항상 명시적이어야 하며, 짧고 결합된 함수 호출보다 가독성을 높여야 합니다.
위 콘텐츠는 『실무로 통하는 클린 코드』에서 내용을 발췌하여 작성하였습니다.
최신 콘텐츠