클린코드 2장 - 의미 있는 이름

박진형·2022년 4월 10일
0

CleanCode

목록 보기
1/8

2장

2장에서는 의미있는 이름에 대해서 다룬다.
우리는 변수, 함수, 인수, 클래스, 패키지, 소스파일, 디렉토리, war, jar 등 많은 곳에 이름을 붙인다. 이름을 잘 지으면 여러모로 편하다는 것과 몇가지 규칙을 소개하고 있다.

의도를 분명히 밝혀라

의도가 분명한 이름을 지으면 절약하는 시간이 많아질것이고 자신을 포함한 코드를 읽는 사람이 행복해 질것이라고 말하고 있다.
변수, 함수, 클래스 등의 이름은 다음과 같은 굵직한 질문에 모두 답할 수 있어야한다.

  • 존재 이유
  • 수행 기능
  • 사용 방법

따로 주석이 필요하다면 의도를 분명히 드러내지 못한 것이다.

예제 1

  • 의도가 분명하지 않은 코드

d 자체는 아무런 의미가 드러나지 않는다.

int d; // 경과 시간(단위: 날짜)
  • 의도가 분명한 코드

의도가 분명해 보인다.

int elapsedTimeDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays

예제 2

  • 의도가 분명하지 않은 코드

코드 맥락이 코드 자체에 명시적으로 드러나지 않는다. 이 코드는 코드를 읽는 사람이 다음과 같은 정보를 안다고 가정해야지 코드의 의도를 알 수 있다.

  • theList에 무엇이 들었는가?
  • theList에서 0번째 값이 어째서 중요한가?
  • 값 4는 무슨 의미인가?
  • 함수가 반환하는 리스트 list1을 어떻게 사용하는가?
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<Cell> getFlaggedCells() {
	List<Cell> flaggedCells = new ArrayList<Cell>();
    for (Cell cell : gameBoard)
    	if(cell.isFlagged())
    		flaggedCells.add(cell);
    return flaggedCells;
}

그릇된 정보를 피하라

그릇된 단서는 코드 의미를 흐리고 나름 널리 쓰이는 의미가 있는 단어를 다른 의미로 사용해서도 안된다.

약어 사용시 주의

ex - hp, aix, sco (hp는 유닉스 플랫폼이나 유닉스 변종을 가리키는 이름이기 때문)
직각 삼각형의 빗변(hypotenuse)을 hp와 같은 약어를 사용하는 것은 위와 같은 의미와 혼동되기 때문에 적합하지 않다.

List

프로그래머에게 List라는 단어는 특수한 의미다. accountList의 자료형(컨테이너)가 List가 아니라면 적합하지 않다. accounts, accountGroup, bunchOfAccounts가 적합하다.

흡사한 이름 주의

A모듈에서 XYZControllerForEfficientHandlingOfStrings라는 이름을 사용하고
B모듈에서 XYZControllerForEfficientStorageOfStrings라는 이름을 사용한다면 차이가 모호해 알아챌 수가 없다.

소문자 l, 대문자 O

소문자 l은 숫자 1처럼 보이고 대문자 O는 숫자 0처럼 보인다.
글꼴을 바꿔 해결할 수도 있겠지만 그것보다는 이름만 바꾸면 해결된다.

의미 있게 구분하라

연속된 숫자를 덧붙이거나 불용어를 추가하는 방식은 적절하지 못하다.

연속된 숫자 사용 X

s1을 source, a2를 destination으로만 고쳐도 더 읽기 좋은 코드가 된다.

public static void copychars(char a1[], char a2[]) {
	for (int i = 0; i < a1.length; i++) {
    	a2[i] = a1[i];
    }
} 

불용어는 사용 X

  • ProductInfo, ProductData의 Info,Data는 의미가 불분명한 불용어다.
  • 불용어는 중복이다.
    • 변수 이름에 variable, 표 이름에 table, 이름에 NameString
    • Name은 부동 소수가 될 가능성이 있는가? 그렇다면 "그릇된 정보를 피하라" 규칙을 위반한다.
  • CustomerObject 클래스와 Customer 클래스가 있다고 하면 고객 급여 이력을 찾으려면 어느 클래스를 찾아야 하는가?
  • getActiveAccount(), getActiveAccounts(), getActiveAccountInfo()
  • moneyAccount와 money
  • customerInfo와 customer
  • accountData와 account
  • theMessage와 message

모두 정확히 구분이 가지 않는다. 읽는 사람이 차이를 알도록 지어야한다.

발음하기 쉬운 이름을 사용하라

genymdhms(generate date, year, month, day, hour, minute, second)와 같은 단어를
"젠 와이 엠 디 에이취 엠 에스"라고 발음하는 것은 적합하지 않다.

genymdhms는 generationTimeStamp
modymdhms는 modificationTimeStamp
pszqint는 recordId 등으로 변경하는것이 좋다.

검색하기 쉬운 이름을 사용하라

문자 하나를 사용하는 이름과 상수는 텍스트 코드에서 쉽게 눈에 띄지 않는다.
MAX_CLASSES_PER_STUDENT는 쉽게 찾을 수 있지만 숫자 7은 찾기 어렵다.
필자는 이름 길이는 범위 크기에 비례해야한다고 주장한다. 변수나 상수를 코드 여러 곳에서 사용한다면 검색하기 쉬운 이름이 바람직하다.

인코딩을 피하라

문제 해결에 집중하는 개발자에게 인코딩은 불필요한 부담이고 발음하기 어려우며 오타가 발생하기 쉬우므로 피해야한다.

  • 과거에는 컴파일러와 IDE의 성능이 좋지 않았기 때문에 변수명에 타입을 붙이는 헝가리안 표기법이 사용 됐다. 지금은 불필요
  • 멤버 변수에 m_이라는 접두어를 붙일 필요가 없다. 클래스와 함수는 접두어가 필요없을 정도로 작아야 마땅하며 IDE 기능으로 충분하다.
  • 인터페이스에 I라는 접두어는 비추천
  • 차라리 구현클래스에 접두어를 붙이는 것을 추천한다 (ShapeFactory, ShapeFactoryImpl 또는 CShapeFactory)

자신의 기억력을 자랑하지 마라

'r' 이라는 변수가 " 호스트와 프로토콜을 제외한 소문자 URL" 이라는 의미이며 이와 비슷한 맥락의 수많은 변수를 모두 기억할 수 있겠는가? 적절하지 않다.

클래스 이름

클래스 이름과 객체 이름은 명사나 명사구가 적합하다.

  • Customer, WikiPage, Account (o)
  • Manager, Processor, Data, Info(x)
  • 동사 (x)

메서드 이름

동사나 동사구가 적합하다.

  • postPayment, deletePage, save (o)
  • 접근자 ,변경자, 조건자는 표준에 따라 get, set, is 붙인다.
  • 생성자를 중복정의할 때는 정적 팩토리 메서드를 사용한다.(생성자 제한시 private생성자)
Complex fulcrumPoint Complex.FromRealNumber(23.0); // O
Complex fulcrumPoint = new Complex(23.0) // △

기발한 이름은 피하라

재미난 이름, 특정 문화에서만 사용하는 농담, 구어체, 속어 등은 피하는게 좋다.

한 개념에 한 단어를 사용하라

똑같은 메서드를 클래스마다 fetch, retrieve, get으로 제각각 부르면 혼란스럽다.
controller, manager, driver도 마찬가지다. 이름이 다르면 클래스도 다르고 타입도 다르다고 생각하기 마련이다.

말장난을 하지마라

  • 두 개의 값을 더하는 함수
  • 집합에 값 하나를 추가하는 함수
    위 두 함수 모두 add라는 메서드를 사용해도 될까?
    맥락이 다르므로 옳지 않다. insert나 append가 적당하다.
    같은 맥락이 아니라도 '일관성'을 고려해 같은 단어를 쓰는 오류를 범하지 말야하 한다.

해법 영역에서 가져온 이름을 사용하라

코드를 읽을 사람도 프로그래머다. 전산 용어, 알고리즘 이름, 패턴 이름, 수학 용어 등을 사용해도 괜찮다.
기술 개념에는 기술 이름이 가장 적합한 선택이다.

문제 영역에서 가져온 이름을 사용하라

적절한 '프로그램 용어'가 없다면 문제 영역에서 이름을 가져온다.
문제 영역 개념과 관련이 깊은 코드라면 문제 영역에서 이름을 가져와야 한다.

의미있는 맥락을 추가하라

대다수의 이름은 스스로의 의미가 분명하지 않다. 그러므로 클래스, 함수, 이름 공간에 넣어 맥락을 부여한다. 모든 방법이 실패하면 마지막 수단으로 접두어를 붙인다.

  • firtName, lastName, street, houseNumber, citry, state, zipcode라는 변수는 전체를 봤을때는 주소라는 사실을 알 수 있지만 state라는 하나만 본다면 주소의 일부라는 사실을 알 수 없다. addr을 접두어로 추가하면 맥락이 분명해 진다.
  • Address라는 클래스를 생성하면 더 좋다.

예제

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라는 변수 세 개가 '통계 추측' 메시지에 사용된다는 것을 알 수 있다.
    -> GuessStatisticsMessage 라는 클래스를 만든 후 세 변수를 클래스에 넣으면 맥락이 분명해 진다.
    -> 그리고 핵심 로직들을 함수로 분리하면 코드가 간결하고 명료해진다.

불필요한 맥락을 없애라

Gas Station Deluxe 라는 애플리케이션을 짠다고 가정할때 모든 클래스의 이름이 GSD로 시작하는것은 옳지않다. MailingAddress 클래스를 추가하면서 GSDAccountAddress로 이름을 바꿨다고 했을 때 다른 고객 관리 프로그램에서 고객 주소가 필요하다면 GSDAccountAddress의 이름으로 가져다 쓰는건 적합하지 않다.

  • 일반적으로 긴 이름이 짧은 이름보다 좋지만, 의미가 분명한 경우에 한해서다. 이름에 불필요한 맥락은 추가하지 않는게 좋다.
  • Address가 조금 더 적합하며 포트 주소, MAC 주소, 웹 주소를 구분해야 한다면 PostalAddress, MAC, URI라는 이름도 괜찮다.

0개의 댓글