본문 바로가기

책책책/Clean Code

[Clean Code] 2장 - 의미 있는 이름

의도를 분명히 밝혀라

좋은 이름을 지으려면 시간이 걸리지만, 좋은 이름으로 절약하는 시간이 훨씬 더 많다. 
의도가 들어나는 이름은 코드 이해화 변경이 쉬워진다.

변수, 함수, 클래스 이름은 다음과 같은 질문에 답할 수 있어야 한다. 

  • 존재의 이유는?
  • 수행 기능은? 
  • 사용 방법은?
  • 따로 주석이 필요하면 의도를 분명히 드러내지 못했다는 말이다.

Example

// Bad
public List<int[]> getThem() {
    List<int[]> list1 = new ArrayList<int[]>();
    for (int[] x : theList) {
        if (x[0] == 4) {
            list1.add(x);
        }
    }
    return list1;
}

위의 예시는 함축성이 부족하다. 
코드 맥락이 코드 자체에 명시적으로 드러나지 않는다. 

// Good
public List<int[]> getFlaggedCells() {
    List<int[]> flaggedCells = new ArrayList<int[]>();
    for (int[] cell : gameBoard) {
        if (cell[STATUS_VALUE] == FLAGGED) {
            flaggedCells.add(cell);
        }
    }
    return flaggedCells;
}

단순히 이름만 고쳤는데도 함수가 하는 일을 이해하기 쉬워졌다. 

그릇된 정보를 피하라

널리 쓰이는 의미가 있는 단어를 다른 의미로 사용하면 안된다. 

  • 여러 계정을 그룹으로 묶을 때, 실제 List가 아니면 accountList라 명명하지 않는다.
  • 프로그래머에게 List라는 단어는 특수한 의미이다. 
  • 차라리 accountGroup, bunchOfAccounts, accounts등으로 명명하라.

서로 흡사한 이름을 사용하지 않도록 주의한다. 

  • 유사한 개념은 유사한 표기법을 사용한다. 

소문자 l 혹은 대문자 O 변수는 숫자처럼 보일 수 있으니 조심하라.

의미있게 구분하라

이름이 달라야 한다면 의미도 달라져야 한다.

  • (a1, a2, …)과 같이 연속적인 숫자를 덧붙인 이름은 아무 정보도 제공하지 못하는 이름이다.
  • 저자 의도가 전혀 드러나지 않는다.

불용어를 사용하지 않는다. 

  • 클래스 이름에 Info, Data와 같은 불용어를 붙이지 말자. 정확한 개념 구분이 되지 않는다.
  • zork라는 변수가 있다는 이유만으로 theZork라고 짓지 말라는 것이다.
  • a나 the 같은 접두어를 사용해서 의미가 분명히 달라진다면 사용해도 무방하다.
    • ex) 지역변수는 a, 함수 인수는 the
  • 불용어는 중복이다.  구분이 되지 않는다.
    • Name VS NameString
    • getActiveAccount() VS getActiveAccounts() VS getActiveAccountInfo() 
    • money VS moneyAmount
    • message VS theMessage

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

발음하기 어려운 이름은 토론하기도 어렵다.

// Bad
class DtaRcrd102 {
    private Date genymdhms;
    private Date modymdhms;
    private final String pszqint = "102";
    /* ... */
};

 

// Good
class Customer {
    private Date generationTimestamp;
    private Date modificationTimestamp;
    private final String recordId = "102";
    /* ... */
};

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

문자 하나를 사용하는 이름과 상수는 텍스트 코드에서 쉽게 눈에 띄지 않는다.
변수나 상수를 여러 곳에서 사용한다면, 검색하기 쉬운 이름이 바람직하다.

// Bad
for (int j=0; j<34; j++) { 
  s += (t[j]*4)/5;
}

 

// Good
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 = (realdays / WORK_DAYS_PER_WEEK);
  sum += realTaskWeeks;
}

 

인코딩을 피하라

헝가리안 표기법

  • 변수명에 해당 변수의 타입(String, Int 등)을 적지않는다.
  • 이름이나 타입을 바꾸기 어려워지며 읽기도 어려워진다.

맴버 변수 접두어

  • 맴버 변수 접두어(m_)를 붙이지 않는다.
  • 클래스와 함수는 접두어가 필요없을 정도로 작아야 한다.
  • 멤버 변수를 다른 색으로 표시해주는 IDE를 사용해라

인터페이스 클래스와 구현 클래스

  • 인터페이스 클래스와 구현 클래스를 나눠야 한다면 구현 클래스의 이름에 정보를 인코딩하자.

 

O / X Interface Class Concrete(Implementation) Class
X IShapeFactory ShapeFactory
O ShapeFactory ShapeFactoryImp
O ShapeFactory CShapeFactory

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

독자가 머리속으로 한번 더 생각해 변환해야 할만한 변수명을 쓰지 말라.

문자 하나만 사용하는 변수 이름은 문제가 있다. 

  • 반복문에서 횟수를 세는 변수는 괜찮다.

클래스 이름

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

  • ex) Customer, WikiPage, Account, AddressParser

Manager, Processor, Data, Info와 같은 단어는 피하고, 동사는 사용하지 않는다.

메서드 이름

메서드 이름은 동사나 동사구가 적합하다. 

  • ex) postPayment, deletePayment, deletePage, save 

접근자, 변경자, 조건자는 자바 빈 표준에 따라 get, set, is로 시작한다.

생성자를 중복 정의할 때는 정적 팩토리 메서드를 사용한다. 
메서드는 인수를 설명하는 이름을 사용한다. 

// 첫번째 보다 두 번째 방법이 Good
Complex fulcrumPoint = new Complex(23.0);  
Complex fulcrumPoint = Complex.FromRealNumber(23.0);

기발한 이름은 피하라

특정 문화에서만 사용되는 재미있는 이름보다 의도를 분명하고 솔직하게 표현하는 이름을 사용하라.

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

추상적인 개념 하나에 단어 하나를 사용하라. 같은 메서드를 클래스마다 제각각으로 부르면 혼란스럽다.

  • ex) fetch, retrieve, get
  • ex) controller, manager, driver

말장난을 하지 마라

한 단어를 두 가지 목적으로 사용하지 마라.

지금까지 구현한 add 메서드는 모두 기존 값 두개를 더하거나 이어서 새로운 값을 만들었다. 
새로 작성하는 add 메서드는 집합에 값 하나를 추가한다. 
이 메서드를 add 라고 불러도 괜찮을까?

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

개발자가 알고 있을 JobQueue, AccountVisitor(Visitor pattern)등의 이름을 사용하지 않을 이유는 없다.
전산용어, 알고리즘 이름, 패턴 이름, 수학 용어 등을 이름으로 선택하라.

음 근데 개인적으로 이 부분은 잘 안와닿음ㅎㅎ

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

문제 영엵 개념과 관련이 깊은 코드라면 문제 영역에서의 이름을 가져와라.

의미있는 맥락을 추가하라

클래스, 함수, 이름 공간을 넣어 맥락(Context)을 표현하라.
그래도 불분명하면 접두어를 사용하라.

// Bad
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);
}

세 변수를 함수 전반에서 사용하고 있다. 

// Good
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";
    }
}

세 변수의 맥락이 분명해졌다.

불필요한 맥락을 없애라

일반적으로 짧은 이름이 긴 이름보다 좋다. 단, 의미가 분명한 경우에 한해서다. 
이름에 불필요한 맥락을 추가하지 않도록 주의한다.

 

'책책책 > Clean Code' 카테고리의 다른 글

[Clean Code] 3장 - 함수  (0) 2021.08.19
[Clean Code] 1장 - 깨끗한 코드  (0) 2021.08.18