읽기 쉬운 코드를 작성하는 것은 프로그래머에게 굉장히 중요한 역량이라고 생각합니다.
프로그래머는 평생 동안 동료들(혹은 미래의 나)과 코드를 통해 소통하기 때문에 수천 수만 라인의 기업 규모의 프로그램일수록 이해하기 쉬운 깔끔한 코드를 짜는 것이 중요하다고 생각합니다.
따라서 미래에 함께 할 동료들과 저를 위한 소통 역량을 키우고자 이 책을 공부하게 되었습니다.
아직 저에게 부족한 부분이 많아 이해가 어려운 부분도 있었습니다. 앞으로 반복적으로 읽으며 제 코드의 문제를 찾아가고 책의 내용을 적용시켜볼 계획입니다.
의도를 분명히 밝혀라
책에서는 의도가 분명한 이름이 정말로 중요하다는 사실을 거듭 강조합니다.
int d; // 경과 시간( 단위 : 날짜 )
위 코드의 문제점은 무엇일까요?
- 이름 d에 아무 의미가 드러나지 않는다. ( 경과 시간이나 날짜라는 느낌이 들지 않는다. )
- 의도를 드러내기 위해 주석이 필요하다.
- 측정하려는 값과 단위를 표현하는 이름이 필요하다.
수정한 코드
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;
- 측정하려는 값과 단위를 표현하는 이름을 사용하여 의도를 드러냈고 코드 이해와 변경이 쉬워졌습니다.
변수나 함수 그리고 클래스 이름은 다음과 같은 굵직한 질문에 모두 답해야 한다.
변수(혹은 함수나 클래스)의 존재 이유는? 수행 기능은? 사용 방법은? 따로 주석이 필요하다면 의도를 분명히 드러내지 못했다는 말이다.
클래스 이름
- 클래스 이름과 객체 이름은 명사구가 적합하다.
- Customer, WikiPage, Account, AddressParser 등이 좋은 예며, Manager, Parser, Data, Info 등과 같은 단어는 피하고, 동사는 사용하지 않는다.
메서드 이름
- 메서드 이름은 동사나 동사구가 적합하다.
- postPayment, deletePage, save 등이 좋은 예다.
- 접근자( Accessor ), 변경자( Mutator ), 조건자( Predicate )는 javabean 표준에 따라 값 앞에 get, set, is를 붙인다.
- 생성자( Constructor )를 중복정의( Overload )할 때는 정적 펙토리 메서드를 사용한다.
String name = employee.getName(); // 접근자
customer.setName("mike"); // 변경자
if ( paycheck.isPosted())... // 조건자
Complex fulcrumPoint = new Complex(23.0); // 생성자 사용
Complex fulcrumPoint = Complex.FromRealNumber(23.0); // 정적 팩토리 메서드 사용
기발한 이름은 피하라
- 재미난 이름보다 명료한 이름을 선택하라.
- 특정 문화에서만 사용하는 농담은 피하는 편이 좋다.
한 개념에 한 단어를 선택하라
- 추상적인 개념 하나에 단어 하나를 선택해 이를 고수한다.
- 똑같은 메서드를 클래스마다 fetch, retrieve, get으로 부른다거나, 동일 코드 기반에 controller, manager, driver를 섞어 쓰면 혼란스럽다. ( 일관성 있는 코드 사용하기 )
의미 있는 맥락을 추가하라
- 클래스, 함수, 이름 공간에 넣어 맥락을 부여한다.
- 모든 방법이 실패하면 마지막 수단으로 접두어를 붙인다.
private void printGuessStatistics(char candidate, int count) {
String number;
String verb;
String pluralModifier;
if(count == 0){
number = "no";
verb = "are";
pluralModifier = "s";
} else if ( count == 1){
number = "1";
verb = "is";
pluralModifier = "";
} else {
number = Integer.toString(count);
verb = "are";
pluralModifier = "s";
}
String guessMessage = String.format(
"There %s %s %s%s", verb, number, candidate, pluralModifier
);
print(guessMessage);
}
왜 위 코드의 변수에 좀 더 의미 있는 맥락이 필요할까요?
- 함수 이름은 맥락 일부만 제공하며, 알고리즘이 나머지 맥락을 제공한다.
- 함수를 끝까지 읽어보고 나서야 number, verb, pluralModifier라는 변수 세 개가 '통계 추측' 메시지에 사용된다는 사실이 드러난다. ( 독자가 맥락을 유추해야 함 )
- 그냥 메서드를 훑어서는 세 변수의 의미가 불분명하다.
수정한 코드
public class GuessStatisticsMessage{
private String number;
private String verb;
private String pluralModifier;
public String make(char candidate, int count ){
createPluralDependentMessageParts(count);
return String.format(
"There %s %s %s%s", verb, number, candidate, pluralModifier);
}
private void createPluralDependentMessageParts(int count){
if (count == 0){
thereAreNoLetters();
} else if (count == 1){
thereIsOneLetter();
} else {
thereAreManyLetters(count);
}
}
private void thereAreManyLettes(int count){
number = Integer.toString(count);
verb = "are";
pluralModifier = "s";
}
private void thereIsOneLetter(){
number = "1";
verb = "is";
pluralModifier = "";
}
private void thereAreNoLetters(){
number = "no";
verb = "are";
pluralModifier = "s";
}
}
- GuessStatisticsMessage라는 클래스를 만들어 세 변수 number, verb, pluralModifier를 클래스에 넣어 세 변수의 맥락을 분명하게 하였습니다.
- 맥락을 개선하면 함수를 쪼개기 쉬워져 알고리즘 또한 더 명확해질 수 있습니다.