
의도가 드러나는 이름을 사용하면 코드의 이해와 변경이 쉬워진다.
아래 예제는 지뢰찾기 게임을 하는 코드이다.
게임판에서 각 칸은 단순 배열로 표현하고, 배열에서 0번째 값은 칸 상태를 뜻한다. 값 4는 깃발이 꽂힌 상태를 가리킨다.
같은 역할을 수행하는데도 이름을 어떻게 붙이느냐에 따라 함수가 하는 일을 이해하기 쉬워진다.
//의도가 보이지 않는 이름(사전정보없이는 의도 파악 어려움)
public List<int[]> getThem() {
List<int[]> list1 = new ArrayList<int[]>();
for (int[] x : theList)
if(x[0] == 4)
list1.add(x);
return list1;
}
//의도가 보이는 이름(이름으로 의도 파악 가능)
public List<int[]> getFlaggedCells() {
List<int[]> flaggedCells = new ArrayList<int[]>();
for (int[] cell : gameBoard)
if(cell[STATUS_VALUE] == FLAGGED)
flaggedCells.add(cell);
return flaggedCells;
}
//이름으로 의도파악 + 직관적으로 코드 분석 가능
public List<Cell> getFlaggedCells() {
List<Cell> flaggedCells = new ArrayList<int[]>();
for (Cell cell : gameBoard)
if(cell.isFlagged()) //명시적인 함수를 사용해 FLAGGED상수 숨김 처리
flaggedCells.add(cell);
return flaggedCells;
}
- 나름 널리 쓰이는 의미가 있는 단어를 다른 단어로 사용하면 안된다.
ex) hp, aix, sco는 변수 이름으로 부적합 (유닉스 플랫폼, 유닉스 변종을 뜻함)- 여러 계정을 그룹으로 묶을 때, 실제 List가 아니라면 accountList라 명명하지 않는다.
ex) accountGroup, Accounts 등으로 명명- 서로 흡사한 이름을 사용하지 않는다.
- 유사한 개념은 유사한 표기법을 사용한다.
컴파일러나 인터프리터만 통과하려는 생각으로 구현하는 코드는 바람직하지 않다. 컴파일러를 통과할지라도 연속된 숫자를 덧붙이거나 불용어를 추가하는 방식은 적절하지 못하다.
이름이 달라야 한다면 의미도 달라져야 한다.
읽는 사람이 차이를 알도록 이름을 지어야 한다.
//잘못된 이름 작성법(의미를 알 수 없는 연속된 숫자 사용)
public static void copyChars(char a1[], char a2[]) {
for(int i = 0; i < a1.length; i++) {
a2[i] = a1[i];
}
}
//올바른 작명의 작성법
public static void copyChars(char source[], char destination[]) {
for(int i = 0; i < source.length; i++) {
destination[i] = source[i];
}
}
//발음하기 어려운 이름의 코드
class DtaRcrd102 {
private Date genymdhms;
private Date modymdhms;
private final String pszqint = "102";
/* ... */
}
//발음하기 쉬운 이름의 코드
class Customer {
private Date generationTimestamp;
private Date modificationTimestamp;
private final String recordId = "102";
/* ... */
}
문자 하나를 사용하는 이름과 상수는 텍스트 코드에서 쉽게 눈에 띄지 않는다. 상수나 한 자리수의 문자는 검색으로 찾아내기 쉽지 않아 버그 수정 역시 어려워질 수 있다.
이름 길이는 범위 크기에 비례해야 한다.
//짧게 작성된 코드
for(int j=0; j<34; j++) {
s += (t[j]*4)/5;
}
//의미 있는 이름으로 지어진 코드
int realDaysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for(int j=0; j < NUMBER_OF_TASKS; j++) {
int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
int realTaskWeeks = (realTaskDays / WORK_DAYS_PER_WEEK);
sum += realTaskWeeks;
}
유형이나 범위 정보까지 인코딩에 넣으면 이름을 해독하기 어려워진다.
초창기 베이식은 글자 하나에 숫자 하나만 허용했으나, 헝가리식 표기법은 기존 표기법을 완전히 개선했다. 그러나 요즘의 프로그래밍언어는 훨씬 많은 타입을 지원하며, 컴파일러가 타입을 기억하고 강제한다.
클래스와 함수는 점차 작아지는 추세로, 변수를 선언한 위치와 사용하는 위치가 멀지 않다.
클래스와 함수는 접두어가 필요없을 정도로 작아야 하며, 멤버 변수를 다른 색상으로 표시하거나 눈에 띄게 보여주는 IDE를 사용해야한다. 접두어를 사용할 경우 옛날에 작성한 구닥다리 코드라는 징표가 되어버린다.
때로는 인코딩이 필요한 경우도 있다. 예를 들어 도형을 생성하는 ABSTRACT FACTORY를 구현한다고 할 때, 이 팩토리는 인터페이스 클래스이며, 구현은 구체 클래스에서 한다. 이 때 두 클래스 명을 지을 때, 인터페이스 클래스 이름과 구현 클래스 이름 중 하나를 인코딩 해야한다면 구현 클래스명을 인코딩하는 편이 좋다.
독자가 코드를 읽으면서 변수 이름을 자신이 아는 이름으로 변환해야 한다면 그 변수 이름은 바람직하지 못하다. 남들이 이해하기 쉬운 코드가 좋은 코드이다.
//메서드는 인수를 설명하는 이름을 사용 > 알아보기 쉽다.
Complex fulcrumPoint = Complex.FormRealNumber(23.0);
Complex fulcrumPoint = new Complex(23.0);의도를 분명하고 솔직하게 표현하라.
일관성 있는 어휘를 사용하라.
코드를 읽을 사람도 프로그래머이므로 기술 개념에는 기술 이름이 가장 적합한 선택이다.
문제 영역 개념과 관련이 깊은 코드라면 문제 영역에서 이름을 가져와야 한다.
스스로 의미가 분명한 이름이 없지 않다. 하지만 대다수 이름은 그렇지 못하므로 클래스, 함수, 이름 공간에 넣어 맥락을 부여한다. 모든 방법이 실패하면 마지막 수단으로 접두어를 붙인다.
아래 예제를 살펴보면, 함수 이름은 맥락 일부만 제공하며, 알고리즘이 나머지 맥락을 제공한다. 함수를 끝까지 읽어보고 나서야 number, verb, pluralModifier라는 변수 세 개가 '통계 추측' 메시지에 사용된다는 사실이 드러난다.
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);
}
위 예제는 함수가 상당히 길고 세 변수를 함수 전반에서 사용한다.
아래는 위 예제를 리팩토링하였다.
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 thereAreManyLetters(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";
}
}
의미가 분명한 경우에 한해 짧은 이름이 긴 이름보다 좋다. 이름에 불필요한 맥락을 추가하지 않도록 주의한다.