가독성 높은 코드를 작성하는 방법 #좋은코드나쁜코드 #5장
* 본 아티클은, '좋은 코드 나쁜 코드' 5장을 기반으로 작성된 글입니다.
코드의 가독성이 좋지 않으면,
다른 개발자들이 코드를 이해하는 데에 시간을 들여야 하고,
버그를 유발할 수 있는 오해를 만들고,
수정이 어려워진다. (수정 이후 제대로 동작하지 않을 수도 있다.)
서술형 명칭 사용
클래스명, 변수명, 함수명(메서드명) 등은 의미를 가지고 있어야 한다.
그리고 이것은 주석문으로 대체될 수 없다.
// 1. 서술적이지 않은 이름
class T {
Set<String> pns = new Set();
...
}
// 2. 서술적이지 않지만, 주석문으로 설명한 코드
/** 팀을 나타낸다. */
class T {
Set<String> pns = new Set(); // 팀에 속한 선수의 이름
...
}
// 3. 서술적인 이름
class Team {
Set<String> playerNames = new Set();
...
}
주석문의 적절한 사용
- 코드가 무엇을 하는지 설명
- 코드가 왜 그 일을 하는지 설명
무엇을 하는지, 왜 그 일을 하는지 등을 요약하는 높은 수준에서의 주석문은 유용하다.
코드만으로 알 수 있는 내용을 설명하는 것은 부적절하다.
주석보다 가독성 높은 코드를 항상 우선시해야 한다.
코드 줄 수, 일관성
간결함은 중요하지만, 정확함이나 가독성이 항상 우선시되어야 한다.
코딩 스타일은 일관적이어야 한다.
중첩을 줄여라
중첩이 깊어지면 가독성이 낮아진다.
중첩된 블록에 반환문이 있다면 조기 반환으로 중첩을 쉽게 낮출 수 있다.
하지만 중첩된 블록에 반환문이 없다면, 그것은 대개 함수가 너무 많은 일을 하고 있다는 신호다.
함수를 분리함으로써 해결할 수 있다.
매개변수의 가독성
sendMessage("hello", 1, true); // 1, true가 무엇을 의미하는지 알기 어렵다.
void sendMessage(String message, int priority, boolean allowRetry) { ... } // 함수의 정의를 확인해야 알 수 있다.
위처럼 함수 정의를 확인해야만 인자의 의미를 알 수 있는 경우가 있다.
이는 명명된 매개변수를 사용함으로 해결할 수 있는데, 모든 언어가 그것을 지원하는 것은 아니다.
그렇다면 서술적 유형, 즉 설명된 이름을 가지고 있는 타입을 사용함으로 그것을 해결할 수 있다.
아래 코드와 같다.
class MessagePriority {
...
MessagePriority(int priority) { ... }
...
}
enum RetryPolicy {
ALLOW,
DISALLOW;
}
void sendMessage(String message, MessagePriority priority, RetryPolicy retryPolicy) { ... }
IDE에서 인자의 타입을 보여주기도 하지만, IDE에 의존할 수 없는 경우도 고려해야 한다.
설명되지 않은 값을 사용하지 마라
매직 넘버 사용을 지양해야 한다.
- 값을 이름이 있는 상수로 바꿀 수도 있고,
- 값을 반환하는 공급자 함수를 사용할 수도 있고,
- 값을 변환하는 헬퍼 함수를 사용할 수도 있다.
Double getKineticEnergyJ() {
return 0.5 *
getMassUsTon() * 907.1847 // Problem : 의미를 알 수 없는 상수
...
}
private const Double KILOGRAMS_PER_US_TON = 907.1847 // 1. 상수 정의
private static Double kilogramsPerUsTon() { // 2. 공급자 함수 정의
return 907.1847;
}
private static Double usTonsToKilograms(Double usTons) { // 3. 헬퍼 함수 정의
return usTons * 907.1847;
}
익명 함수는 적절하게 사용해야 한다
익명 함수는 간단한 로직에 사용해야 한다.
익명 함수는 이름을 가지고 있지 않기 때문에, 가독성이 떨어질 염려가 있다.
길이가 너무 길어지거나, 로직이 복잡해지는 경우,
명명된 여러 개의 함수로 나눠야 한다.
새 기능을 적절히 사용하라
자바의 스트림을 사용하면, 복잡한 코드를 함수형 스타일로 간결하게 표현할 수 있다.
하지만 불필요한 사용은 지양해야 한다.
아래 코드는 맵 자료구조 에서 key 값으로 value를 검색한다.
String value = map.get(key)
그런데 괜히 스트림을 사용하겠다고 아래처럼 코드를 복잡하게 만들 수 있다.
String value = map.entrySet().stream()
.filter(entry -> entry.getKey().equals(key))
.map(Entry::getValue)
.findFirst()
.orElseGet(() -> "hi")
또, 새로운 기능은 협업하는 팀원들 전체에 공유된 내용이어야 한다.
개선사항보다 팀원들의 수고가 더 커지지는 않을지 고민해야 한다.