3. Shatter Long Functions
내용 정리
3.1 첫 번째 규칙: 왜 다섯 줄인가?
3.3.1 규칙: 다섯 줄 제한
정의: 메서드는 ({ 및 })를 제외하고 5줄 이상이 되어서는 안 된다.
스멜: 메서드가 길다는 것 자체가 스멜이다. 여기서 5줄 제한을 지키려다가 SRP(메서드는 한 가지 작업만 해야한다)를 지키는게 어려워질수도 있다. → 예외적으로 line 수를 변경해 줄 수도 있겠지만, 보통은 5줄 정도로 끝나는 경우가 많다.
의도: 관심을 가지지 않으면 시간이 지남에 따라 더 많은 기능이 추가되면서 메서드가 커지는 경향이 있고, 그로 인해 코드를 점점 더 이해하기 어려워진다.
참조: 메서드 추출 리팩터링을 참고하자
1 | fun containsEven(arr: List<List<Int>>): Boolean { |
3.2 함수 분해를 위한 리팩터링 패턴 소개
- 코드를 이해하기 위해 함수명을 고려하라
- 코드의 ‘형태’ 를 살펴보자
3.2.1 리팩터링 패턴: 메서드 추출
설명: 한 메서드의 일부를 취해서 자체 메서드로 추출합니다.
절차
- 추출할 줄의 주변을 빈 줄로 표시하는데, 주석으로 표시할 수도 있습니다.
- 원하는 이름으로 새로운 빈 메서드를 만듭니다.
- 그룹의 맨 위에서 새로운 메서드를 호출합니다.
- 그룹의 모든 줄을 선택해서 잘라내어 새로운 메서드의 본문에 붙여 넣습니다.
- 컴파일합니다.
- 매개변수를 도입하여 호출하는 쪽의 오류를 발생시킵니다.
- 이러한 매개변수 중 하나를 반환 값으로 할당해야 할 경우
- 새로운 메서드의 마지막에 return p;를 추가합니다.
- 새로운 메서드를 호출하는 쪽에서 p = newMethod(…)와 같이 반환 값을 할당합니다.
- 컴파일합니다.
- 호출 시 인자를 전달해서 오류를 잡습니다.
- 사용하지 않는 빈 줄과 주석을 제거합니다.
예제: 함수 중간에 if문으로 감싸여져 있는 코드를 메서드로 추출하여 그 결과를 이용하는 리팩터링이 들어가 있다.
3.3 추상화 수준을 맞추기 위한 함수 분해
3.3.1 규칙: 호출 또는 전달, 한 가지만 할 것
정의: 함수 내에서는 객체에 있는 메서드를 호출하거나 객체를 인자로 전달할 수 있지만 둘을 섞어 사용해서는 안 된다.
설명: 더 많은 메서드를 도입하고 여러 가지를 매개변수로 전달하기 시작하면 결국 책임이 고르지 않게 될 수 있다. 동일한 수준의 추상화를 유지하는 편이 코드를 읽기가 더 쉬워진다.
- 변경 전
1 | function average(arr: number[]) { |
- 변경 후
1 | function average(arr: number[]) { |
스멜: ‘함수의 내용은 동일한 추상화 수준에 있어야 한다’ (클린코드에도 있음)
- 찾는 법?: 인자로 전달된 변수 옆 ‘.’ 으로 찾아보자
의도: 메서드에서 몇 가지 세부적인 부분을 추출해서 추상화를 도입하면 이 규칙은 연관된 다른 세부적인 부분도 추출하게 한다. 메서드 내부의 추상화 수준이 항상 동일하게 유지된다.
참조: 메서드 추출 리팩터링 패턴을 참조
3.3.2 규칙 적용
- 내가 작성한 Kotlin Multiplatform에서는 적용이 안되어서 주석만 남기고 패스
3.4 좋은 함수 이름의 속성
- 정직해야 한다. 함수의 의도를 설명해야 한다.
- 완전해야 한다. 함수가 하는 모든 것을 담아야 한다.
- 도메인에서 일하는 사람이 이해할 수 있어야 한다. 작업 중인 도메인에서 사용하는 단어를 사용해라. 그렇게 하면 의사소통이 더욱 효율적이게 되고 팀원 및 고객과 코드에 대해 더 쉽게 이야기할 수 있다는 장점이 있다.
3.5 너무 많은 일을 하는 함수 분리하기
3.5.1 규칙: if 문은 함수의 시작에만 배치
정의: if 문이 있는 경우 해당 if 문은 함수의 첫 번째 항목이어야 한다.
- 변경 전
1 | fun reportPrimes(n : Int) { |
- 변경 후
1 | fun reportPrimes(n : Int) { |
스멜: 다섯 줄 제한과 같이, 이 규칙은 함수가 한 가지 이상의 작업을 수행하는 스멜을 막기 위해 존재한다.
의도: if 문이 하나의 작업이기 때문에 이를 분리할 때 이어지는 else if는 if 문과 분리할 수 없는 원자 단위로 본다.
3.5.2 규칙 적용
요약
- 다섯 줄 제한 규칙은 메서드는 다섯 줄 이하여야 한다는 말
- 호출 또는 전달, 한 가지만 할 것: 하나의 메서드 내에서 객체에 있는 메서드를 호출하거나 객체를 매개변수로 전달할 수 있지만, 둘 다 해서는 안 된다는 말
- 메서드 이름은 투명하고 완전해야 하며 이해할 수 있어야 한다. 메서드 추출을 사용하면 가독성도 올라간다.
- if 문은 함수의 시작에만 배치: if를 사용해 조건을 확인하는 경우 한 가지 작업만 수행하므로 메서드가 다른 작업을 수행하지 못하게 한다.
느낀점
- 3.5.1 규칙(if 문은 함수의 시작에만 배치) 은 해본적이 없지만 정말 괜찮은 방법인 것 같다.
- 3.3의 추상화 수준을 맞추는 리팩터링도 말만 들어봤지 이 규칙을 적용하려고 리팩토링을 수행했던 적은 거의 없던 것 같다.
- 나머지 리팩터링 패턴에 대해서는 이미 자주하고 있는 방법들이었지만, 무의식적으로 하기 보다 구체적으로
어떤 상황에서 이렇게 리팩터링을 해라!
를 알려주니까 더 좋았다. - 빨리 기존 코드들을 리팩터링 하고 싶어졌다.
짧게나마 리팩터링을 해보았다
https://github.com/ZeroWastePortenday/green_life_app/commit/fe5e01fb9b988e47c7e6a5a013d24569bfa2b19a
리팩터링 예정 1
→ https://play.google.com/store/apps/details?id=com.yeolsimee.moneysaving
Android 앱으로만 되어 있는데 이번에 Flutter 앱으로 다시 만들어서 iOS 동시 배포예정
리팩터링 예정 2
https://github.com/boring-km/color-from-image