2장 의미 있는 이름

클린 코드 2장을 요약한 내용 입니다.

의도를 분명히 밝혀라

좋은 이름을 지으려면 시간이 걸리지만 좋은 이름으로 절약하는 시간이 훨씬 더 많다. 변수함수 그리고 클래스 이름은 다음과 같은 질문에 모두 답해야 한다.

  • 변수(혹은 함수나 클래스)의 존재 이유는?

  • 수행 기능은?

  • 사용 방법은?

주석이 필요하다면 의도를 분명히 드러내지 못했다는 말이다.

다음 코드는 무엇을 할까?

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

위 코드는 코드 맥락이 코드 자체에 명시적으로 드러나지 않는다.

  • thisList에 무엇이 들어있는가?

  • thisList에서 0번째 값이 어째서 중요한가?

  • 4는 무슨 의미인가?

  • 함수가 반환하는 리스트 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;
}

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

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

class DtaRcd102 {
		// generate date, year, month, day, hour, minutes, seconds
		private Date genymdhms;
		private Date modymdhms;
		private final String pszqint = "102"
}

A> 젠 와이 엠 디 에이취 엠 에스는 어떤 값이 들어가나요?

B> 젠 야 무다 힘즈는 말씀이신가요?

변수명을 발음하기 쉽게 수정해보자.

class Customer {
		// generate date, year, month, day, hour, minutes, seconds
		private Date generationTimeStamp;
		private Date modificationTimestamp;
		private final String recordId = "102"
}

A> 펭수씨, 이 레코드 좀 보세요. "Generation Timestamp" 값이 내일 날짜입니다. 어떻게 이럴수 있죠?

B> 아, 잘못 들어간거 같네요. 오늘 날짜로 수정하겠습니다.

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

문자 하나를 사용하는 이름상수는 텍스트 코드에서 쉽게 눈에 띄지 않는다는 문제점이 있다. MAX_CLASSES_PER_STUDENT는 찾기가 쉽지만, 숫자 7은 은근히 찾기 어렵고 의미를 해석하기도 어렵다.

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

위 코드를 보면 몇가지 해석하기 어려운 부분이 있다.

  • 34는 무엇을 의미하는지?

  • 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;
}

상수를 의미있는 변수로 표현하면 위와 같이 좀 더 해석하기 쉽다.

  • 일한 일수를 구한다. (일한 시간 * 실제 일한 일수)

  • 일한 일수를 5일로 나누어 총 일한 주를 구한다.

  • 총일한 주를 더해준다.

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

추상적인 개념 하나에 단어 하나를 선택해 이를 고수한다. 예를 들어, 똑같은 메서드를 클래스마다 fetch, retrive, get으로 제각각 부르면 혼란스럽다. 어느 클래스에서 어느 이름을 썼는지 기억하기 어렵다. 따라서 메서드 이름은 독자적이고 일관적이어야 한다.

Q. 그렇다면 이렇게 사용하는건 어떨까..?

클래스 이름이 명확하다면 클래스에서 수행하는 메서드는 도메인이나 메뉴명을 넣지 않아도 되지 않을까?

// option 1. 메서드에도 메뉴를 표현
public class MarketplaceService {
		public List<Marketplace> getMarketplaceList() { ... }
		public void saveMarketplace(Marketplace marketplace) { ... }
		public void updateMarketplace(Marketplace marketplace) { ... }
		public void deleteMarketplace(Integer marketplaceId) { ... }
}

// option 2. 메서드에는 메뉴를 생략
public class MarketplaceService {
		public List<Marketplace> getItems() { ... }
		public void save(Marketplace marketplace) { ... }
		public void update(Marketplace marketplace) { ... }
		public void delete(Integer marketplaceId) { ... }
}

말장난을 하지 마라

한 단어를 두 가지 목적으로 사용하지 마라. 예를 들어, 여러 클래스에 add하는 메서드가 생겼다. 지금까지 구현한 add 메서드는 모두가 기존 값 두개를 더하거나 이어서 새로운 값을 만든다고 가정하자.

public int add(int num1, int num2) {
		return num1 + num2;
}

그런데 새로 작성하는 메서드는 집합에 값 하나를 추가한다.

이 메서드에도 add라 불러도 괜찮을까?

add라는 메서드가 많으므로 일관성을 지키려면 add라 불러야 하지 않을까? 하지만 새 메서드는 기존 add 메서드와 맥락이 다르다. 그러므로 insertappend라는 이름이 적당하다.

프로그래머는 코드를 최대한 이해하기 쉽게 짜야 한다. 집중적인 탐구가 필요한 코드가 아니라 대충 훑어봐도 이해할 코드 작성이 목표다.

의미 있는 맥락을 추가하라

스스로 의미가 분명한 이름이 없지 않다. 하지만 대다수 이름은 그렇지 못하다. 예를 들어, firstName, lastName, street, houseNumber, city, state, zipcode 라는 변수가 있다. 변수를 훑어보면 주소라는 사실을 금방 알아챈다.

하지만 어느 메서드가 state라는 변수 하나만 사용한다면?

사용하는 이는 변수 state가 주소 일부라는 사실을 금방 알수 있을까?

addr라는 접두어를 추가해 addrFirstName, addrLastName, addrState라 쓰면 맥락이 좀 더 분명해진다. 변수가 좀 더 큰 구조에 속한다는 사실이 적어도 독자에게는 분명해진다. 물론 Address라는 클래스를 생성하면 더 좋다. 그러면 변수가 좀 더 큰 개념에 속한다는 사실이 컴파일러에게도 분명해진다.

다음은 갯수에 따라 다르게 표현되는 표현법을 제공하는 기능이다.

private void printGuessStatistics(char candiate, 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);
}

일단 함수가 좀 길다. 그리고 세 변수를 함수 전반에서 사용하다. 함수를 작은 조각으로 쪼개고자 GuessStaticsMessage라는 클래스를 만든 후 세 변수를 클래스에 넣었다. 그러면 세 변수는 맥락이 분명해진다. 즉, 세 변수는 확실하게 GuessStaticsMessage에 속하게 된다. 이렇게 맥락을 개선하면 함수를 쪼개기가 쉬워지므로 알고리즘도 좀 더 명확해진다.

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

결론

좋은 이름을 선택하려면 설명 능력이 뛰어나야 하고 문화적인 배경이 같아야 한다. 우리들 대다수는 자신이 짠 클래스 이름과 메서드 이름을 모두 암기하지 못한다. 여느 코드 개선 노력과 마찬가지로 이름 역시 나름대로 바꿨다가는 누군가 질책할지도 모른다. 그렇다고 코드를 개선하려는 노력을 중단해서는 안 된다.

Last updated