가독성의 핵심은 개발자가 코드의 기능을 빠르고 정확하게 이해할 수 있도록 하는 것이다.
class T {
Set<String> pns = new Set();
Int s = 0;
Boolean f(String n) {
return pns.contains(n);
}
...
}
숨막힌다...
/** 팀을 나나낸다. */
class T {
Set<String> pns = new Set(); // 팀에 속한 선수의 이름
Int s = 0; // 팀의 점수
/**
* @param n 플레이어의 이름
* @return true 플레이어가 팀에 속해 있는 경우
*/
Boolean f(String n) {
return pns.contains(n);
}
...
}
class Team {
Set<String> playerNames = new Set();
Int score = 0;
Boolean containsPlayer(String playerName) {
return playerNames.contains(playerName);
}
...
}
String generateId(String firstName, String lastName) {
// "{이름}.{성}"의 형태로 ID를 생성한다.
return firstName + "." + lastName;
}
주석문을 사용해서 코드가 하는 일을 설명하지만 코드 자체로 설명이 되기 때문에 주석문은 쓸모가 없다.
String generateId(String[] data) {
// data[0]는 유저의 이름이고 data[1]은 성이다.
// "{이름}.{성}"의 형태로 ID를 생성한다.
return data[0] + "." + data[1];
}
🍠🍠🍠
String generateId(String[] data) {
return firstName(data) + "." + lastName(data);
}
String firstName(String[] data) {
return data[0];
}
String lastName(String[] data) {
return data[1];
}
특정 코드가 존재하는 이유나 어떤 일을 수행하는 목적은 코드를 파악하고자 하는 다른 개발자가 알 수 없는 배경 상황이나 지식과 관련 있을 수 있다.
이러한 배경 상황이나 지식이 코드를 이해하거나 안전하게 수정하기 위해 중요한 경우 주석문은 매우 유용하다.
class User {
private final Int username;
private final String firstName;
private final String lastName;
private final Version signupVersion;
...
String getUserId() {
if (signupVersion.isOlderThan("2.0")) {
// (v2.0 이전에 등록한) 레거시 유저는 이름으로 ID가 부여된다.
// 자세한 내용은 #4218 이슈를 보라.
return firstName.toLowerCase() + ".". + lastName.toLowerCase();
}
// (v2.0 이후로 등록한) 새 유저는 username으로 ID가 부여된다.
return username;
}
...
}
/**
* 스트리밍 서비스의 유저에 대한 자세한 사항을 갖는다.
*
* 이 클래스는 데이터베이스 직접 연결하지 않는다. 대신 메모리에 저장된 값으로 생성된다.
* 따라서 이 클래스가 생성된 이후에 데이터베이스에서 이뤄진 변경 사항을 반영하지 않을 수 있다.
*/
class User {
...
}
일반적으로 코드베이스의 코드 줄 수는 적을수록 좋다.
그러나 코드 줄 수는 우리가 실제로 신경 쓰는 것들을 간접적으로 측정해줄 뿐이다.
우리가 정말로 신경 쓰는 것은 코드에 대해 다음과 같은 사항들을 확실하게 하는 것이다.
Boolean isIdValid(UInt16 id) {
return countSetBits(id & 0x7FFF) % 2 == ((id & 0x8000) >> 15);
}
Boolean isIdValid(UInt16 id) {
return extractEncodeParity(id) == calculateParity(getIdValue(id));
}
private const UInt16 PARITY_BIT_INDEX = 15;
private const UInt16 PARITY_BIT_MASK = (1 << PARIT_BIT_INDEX);
private const UInt16 VALUE_BIT_MASK = ~PARITY_BIT_MASK;
private UInt16 getIdValue(UInt16 id) {
return id & VALUE_BIT_MASK;
}
private UInt16 extractEncodeParity(UInt16 id) {
return (id & PARITY_BIT_MASK) >> PARITY_BIT_INDEX;
}
// 패리티 비트는 1인 비트의 수가 짝수이면 0이고
// 홀수이면 1이다.
private UInt16 calculateParity(UInt16 value) {
return countSetBits(value) % 2;
}
많은 함수 인수
함수 호출은 인수의 개수가 늘어나면 이해하기 힘들어진다.
sendMessage("hello", 1, true);
void sendMessage(String message, Int priority, Boolean allowRetry) {
...
}
sendMessage(message: "hello", priority: 1, allowRetry: true);
최근에 나온 언어에서 지원되고 있다.
모든 언어가 명명된 매개변수를 지원하는 것은 아니다.
객체 구조 분해(object destructuring)
를 사용하는 타입스크립트 및 다른 형태의 자바스크립트에서 흔히 볼 수 있다.
interface SendMessageParams {
message: string,
priorty: number,
allowRetry: boolean,
}
class MessagePriority {
...
MessagePriority(Int priority) { ... }
...
}
enum RetryPolicy {
ALLOW_RETRY,
DISALLOW_RETRY
}
void sendMessage(
String message,
MessagePriority priority,
RetryPolicy allowRetry) {
...
}
✅ 이 함수에 대한 호출은 함수 정의를 알지 못해도 이해하기 쉽다.
sendMessage("hello", new MessagePriority(1), RetryPolicy.ALLOW_RETRY);