2장 - 의미 있는 이름
소프트웨어에서 이름은 어디나 쓰인다. 변수, 함수, 인수, 클래스, 소스파일, 디렉터리 등등. 그러므로 이름을 잘 지으면 여러모로 편하다.
기본적인 명명법
클래스 이름
클래스 이름과 객체 이름은 명사
나 명사구
가 적합하다. 동사는 지양한다.
- 좋은 예: Customer, WikiPage, Account, AddressParser
- 피할 단어: Manager, Processor, Data, Info
메서드 이름
메서드 이름은 동사
나 동사구
가 적합하다.
- 좋은 예: postPayment, deletePage, save
접근자, 변경자, 조건자는 앞에 get, set, is를 붙인다.
const name = employee.getName();
customer.setName('mike');
if (paycheck.isPosted()) ...
메서드는 인수를 설명하는 이름
을 사용한다.
// good
const fulcrumPoint: Complex = Complex.FromRealNumber(23.0);
// better - 클래스 생성의 경우 생성자 사용
const fulcrumPoint: Complex = new Complex(23.0);
Rules
의도를 분명히 밝혀라
의도가 분명한 이름은 다음과 같은 질문에 모두 답할 수 있어야 한다.
- 변수(or 함수/클래스)의 존재 이유
- 수행 기능
- 사용 방법
예시 1)
// bad - 아무 의미도 드러내지 않음
let d;
// good - 측정하려는 값, 단위를 표현함
let elapsedTimeInDays;
let daysSinceCreation;
let daysSinceModification;
let fileAgeInDays;
예시 2)
// bad - 코드의 맥락이 코드 자체에 명시적으로 드러나지 않는다.
function getThem() {
let list1 = [];
for (const x of theList) {
if (x[0] === 4) list1.push(x);
}
return list1;
}
// good - 각 개념에 이름이 붙어있어서 역할을 파악하기 쉽다.
function getThem() {
let flaggedCells = [];
for (const cell of gameBoard) {
if (cell[STATUS_VALUE] === FLAGGED) flaggedCells.push(cell);
}
return flaggedCells;
}
// better - isFlagged라는 명시적인 함수를 사용해 FLAGGED라는 상수를 감췄다.
function getThem() {
let flaggedCells = [];
for (const cell of gameBoard) {
if (cell.isFlagged()) flaggedCells.push(cell);
}
return flaggedCells;
}
그릇된 정보를 피하라
헷갈리게 만들거나, 오해의 소지가 있는 단어 피하기
- 널리 쓰이는 이름을 다른 의미로 사용하면 안된다.
- 서로 흡사한 이름 지양
- ex)
XYZControllerForEfficientHandlingOfStrings
vsXYZControllerForEfficientStorageOfStrings
- ex)
- 이름으로 그릇된 정보 제공하지 않게 주의
- O와 0, 1과 l
- 그룹으로 묶을 때, 실제 컨테이너가
List
가 아님에도 이름에 List를 붙이는 경우
- 유사한 개념은 유사한 표기법을 사용한다. 즉, 일관성 있게 표기한다.
의미 있게 구분하라
- 연속된 숫자 피하기
- a, b, c, ..., a1, a2, a3, ... 등
- 저자의 의도가 드러나지 않음. 아무런 정보도 제공하지 못함
- 불용어 피하기
- Info, Data 등은 의미가 불분명한 불용어임
- 변수 이름에 variable, 표 이름에 table 등 금물
- 읽는 사람이 차이를 알도록 이름을 지어라
// bad - 용도의 차이를 구분할 수 없음 getActiveAccount(); getActiveAccounts(); getActiveAccountInfo();
발음하기 쉬운 이름을 사용하라
프로그래밍은 사회 활동이기 때문에 발음하기 쉬운 이름은 중요하다.
// bad
let genymdhms; // generated-ymd-hms의 약어임은 알겠으나 발음을 알 수 없음
let modymdhms;
const pszqint = "102";
// good
let generationTimeStamp;
let modificationTimeStamp;
const recordId = "102";
검색하기 쉬운 이름을 사용하라
- 검색 관점에서 긴 이름이 짧은 이름보다 좋다. 검색이 쉬운 이름이 상수보다 좋다.
let sum; let WORK_DAYS_PER_WEEK; // better
- 간단한 메서드에서 로컬 변수만 한 문자를 사용한다.
- 이름의 길이는 범위 크기에 비례해야 한다.
- 변수나 상수를 코드 여러 곳에서 사용한다면 검색하기 쉬운 이름이 바람직하다.
인코딩을 피하라
변수명에 굳이 유형이나 범위 정보까지 인코딩하지 말자.
- 헝가리식 표기법 지양하기 (변수명에 타입을 적지 말자)
let phoneString: PhoneNumber; // 타입이 바뀌어도 이름은 바뀌지 않는다
- 접두어(또는 접미어)는 구닥다리 코드다.
let m_dsc; // bad let description; // good
- 인터페이스 클래스와 구현 클래스
- 인터페이스 이름은 접두어(I)를 붙이지 않는 편이 좋다.
- 인터페이스 클래스 이름과 구현 클래스 이름 중 하나를 인코딩해야 한다면 구현 클래스 이름을 선택한다. ShapeFactoryImp나 CShapeFactory가 IShapeFactory보다 좋다.
자신의 기억력을 자랑하지 마라
- 명료함이 최고
- 남들이 이해하는 코드를 작성하는 것이 더 중요하다.
- 문자 하나만 사용하는 변수 이름은 피하라. 단, 루프에서 반복 횟수 변수는 전통적으로 한 글자 (i, j, k)이므로 제외한다. l은 절대 안된다 (1과 헷갈림)
기발한 이름은 피하라
- 재미난 이름보다 명료한 이름을 선택하라.
- HolyHandGrenade (X), DeleteItems (O)
- 의도를 분명하고 솔직하게 표현하라.
한 개념에 한 단어를 사용하라
- 일관성 있는 어휘
- 똑같은 메서드를 클래스마다 fetch, retrieve, get으로 제각각 부르면 혼란스럽다. 마찬가지로 동일 코드 기반에 controller, manager, driver를 섞어쓰면 혼란스럽다.
말 장난을 하지 마라
- 다른 개념에 같은 단어를 사용하지 마라.
- add 메서드가 두 개를 이어 붙여 새로운 값을 만든다고 가정하자. 집합에 값 하나를 추가하는 메서드는 기존 add 메서드와 맥락이 다르므로 insert, append 등의 다른 이름을 써야 한다.
해법 영역에서 가져온 이름을 사용하라
- 전산 용어, 알고리즘 이름, 패턴 이름, 수학 용어 등을 사용해도 괜찮다. 기술 개념에는 기술 이름이 가장 적합한 선택이다.
- 개발자라면
JobQueue
,AccountVisitor(VISITOR 패턴)
을 알고 있으니 이름에 사용해도 무방하다.
문제 영역에서 가져온 이름을 사용하라
- 적절한 프로그래머 용어가 없다면 문제 영역에서 이름을 가져온다.
- 문제 영역 개념과 관련이 깊은 코드라면 문제 영역에서 이름을 가져와야 한다.
의미 있는 맥락을 추가하라
변수를 클래스, 함수, 이름 공간(Namespace)에 넣어 맥락을 부여한다. 최후의 수단으로 접두어를 붙인다.
// bad
function printGuessStatistics(candidate, count) {
let number;
let verb;
let pluralModifier;
if (count === 0) {
number = "no";
verb = "are";
pluralModifier = "s";
} else if (count === 1) {
number = "1";
verb = "is";
pluralModifier = "";
} else {
number = count.toString();
verb = "are";
pluralModifier = "s";
}
const guessMessage = `There ${verb} ${number} ${candidate}${pluralModifier}`;
console.log(guessMessage);
}
// good
class GuessStatisticsMessage {
make(candidate, count) {
createPluralDependentMessageParts(this.count);
return `There ${this.verb} ${this.number} ${this.candidate}${this.pluralModifier}`;
}
createPluralDependentMessageParts(count) {
if (count === 0) {
thereAreNoLetters();
} else if (count === 1) {
thereIsOneLetter();
} else {
thereAreManyLetters(count);
}
}
thereAreManyLetters(count) {
this.number = count.toString();
this.verb = "are";
this.pluralModifier = "s";
}
thereIsOneLetter() {
this.number = "1";
this.verb = "is";
this.pluralModifier = "";
}
thereAreNoLetters() {
this.number = "no";
this.verb = "are";
this.pluralModifier = "s";
}
}
불필요한 맥락을 없애라
고급 휘발유 충전소(Gas Station Deluxe)
라는 애플리케이션을 짠다고 가정하자.- 모든 클래스 이름 앞에
GSD
를 붙이는 것은 좋지 않다. IDE를 방해하기까지 한다. GSD
모듈에 MailingAddress 클래스를 추가하면서 GSDAccountAddress로 이름을 바꿨다. 나중에 다른 고객 관리 프로그램에서 고객의 주소가 필요할 때, 이 GSDAccountAddress 클래스를 사용할까?- 의미가 분명한 경우에 한해서 짧은 이름이 긴 이름보다 좋다.
마치면서
좋은 이름을 선택하는 것은 어렵다.
설명 능력이 뛰어나야 하고 문화적인 배경이 같아야 하기 때문이다.
사람들이 이름을 바꾸지 않으려는 이유 하나는 다른 개발자가 반대할까 두려워서다.
그러나 코드를 개선하려는 노력을 중단해서는 안되며, 우리는 쉽게 읽히는 코드를 짜는데 집중해야한다.