[Clean Code] TIL #3

kdkdhoho·2022년 2월 20일
0

Clean Code

목록 보기
2/6

2장. 의미 있는 이름

목차

  1. 1. 의도를 분명히 밝혀라
  2. 2. 그릇된 정보를 피하라
  3. 3. 의미있게 구분하라
  4. 4. 발음하기 쉬운 이름을 사용하라
  5. 5. 검색하기 쉬운 이름을 사용하라
  6. 6. 인코딩을 피하라
  7. 7. 자신의 기억력을 자랑하지 마라
  8. 8. 클래스 이름
  9. 9. 메서드 이름
  10. 10. 기발한 이름은 피하라
  11. 11. 한 개념에 한 단어를 사용하라
  12. 12. 말장난을 하지 마라
  13. 13. 해법 영역에서 가져온 이름을 사용하라
  14. 14. 문제 영역에서 가져온 이름을 사용하라
  15. 15. 의미있는 맥락을 추가하라
  16. 16. 불필요한 맥락을 없애라

1. 의도를 분명히 밝혀라

변수나 함수 그리고 클래스 이름은 다음과 같은 굵직한 질문에 모두 답해야 한다.
존재 이유는? 수행 기능은? 사용 방법은?
따로 주석이 필요하다면 의도를 분명히 드러내지 못했다는 말이다.

주석없이도 이름을 보고 무엇인지 알 수 있도록 하자

int d; // 경과 시간(단위: 날짜)
위 코드에서 d는 아무 의미를 드러내지 않는다.

int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;

와 같이 직접적으로 의도를 표현하자.

이제 지뢰찾기 게임을 만든다고 가정하자.
게임판에서 각 칸은 단순 배열로 표현하고, 배열에서 0번 째 값은 칸 상태를 뜻하고, 값 4는 깃발이 꽂힌 상태를 가리킨다고 가정하고 아래 두 코드를 비교해보자.

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

vs

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

위 코드는 코드를 해석하는 기분이 들고, 아래 코드는 글을 읽는 듯한 기분이 들지 않는가?
변수명을 의도가 분명하게만 했음에도 가독성이 훨씬 증가한다.

여기서 더 나아가, int[]배열을 사용하는 대신에 칸을 간단한 클래스로 만들어도 좋다.
isFlagged라는 명시적인 함수를 사용해 FLAGGED라는 상수를 감춰도 좋다.

public List<Cell> getFlaggedCells() {
	List<Cell> flaggedCells = new List<Cell>();
	for(int[] cell : gameBoard)
		if(isFlagged())
			flaggedCells.add(cell);
	return flaggedCells;
}

2. 그릇된 정보를 피하라

  • 프로그래머는 코드에 잘못된 정보를 남겨선 안된다.
    잘못된 정보는 코드의 의미를 흐린다.
    나름대로 널리 쓰이는 의미가 있는 단어를 다른 의미로 사용해도 안된다.

    예를 들어, 직각삼각형의 빗변인 hypotenuse를 구현할 때는 hp가 괜찮은 약어로 보일지라도 hp라는 변수는 독자에게 잘못된 정보를 준다.

  • 또한 여러 계정을 그룹으로 묶을 때, 실제 List가 아니라면, accountList는 피하자.
    계정을 담는 컨테이너가 실제 List가 아니라면 프로그래머에게 그릇된 정보를 피하는 셈이다.
    만약, 실제 컨테이너가 List인 경우라도 컨테이너 유형을 이름에 넣지 않는 편이 바람직하다.

  • 서로 흡사한 이름을 사용하지 않도록 주의하자.
    개발자는 대부분 객체에 달린 상세한 주석이나 클래스가 제공하는 메소드 목록을 살펴보지 않은 채 이름만 보고 객체를 선택한다.
    따라서 이름으로 하여금 잘못된 정보를 얻을 수 있는 가능성을 배제하자.

    한 예로,

    int a = 1;
    if ( O == 1)
    a = O1;
    else
    l = 01;

    과 같이 l과 O, 그리고 1과 0으로 인해 코드 가독성을 확 떨어트리는 경우가 있다.

3. 의미있게 구분하라

컴파일러나 인터프리터만 통과하려는 생각으로 코드를 구현하는 프로그래머는 스스로 문제를 일으킨다.
연속된 숫자를 덧붙이거나 noise word(불용어)를 추가하는 방식은 적절하지 못하다.

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

보다

public static void copyChars(char source[], char destination[]) {
	for(int i = 0; i < source.length; i++) 
    	destination[i] = source[1];
}

이 좀 더 명확하다.

noise word를 추가한 이름 역시 아무런 정보를 제공하지 못한다.
Product라는 클래스가 있다고 가정해보자. 여기서 ProductData와 ProductInfo 클래스를 추가한다면, 다른 사람이 이 코드를 봤을 때 이 세 가지를 알아볼 수 있을까?
읽는 사람이 차이를 알아볼 수 있도록 이름을 짓자.

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

저자의 예로, 한 회사에서 변수명을 genymdhms라고 사용한다. generate date, year, month, day, hour, minute, second라는 뜻이다.
과연 이게 상식적으로 좋은 이름일까?

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

vs

class DtaRcrd102 {
  private Date generateTimeStamp;
  private Date modifyTimeStamp;
  private final String recordId = "102";
  /* ... */
};

두 코드 중 어떤 것이 상식적일까?

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

여기서 검색은 코드상에서의 검색이다.

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

int realDaysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sun = 0;

for(int i=0; i < NUMBER_OF_TASKS; i++) {
  int realTaskDays = taskEstimate[i] * realDaysPerIdealDay;
  int realTaskWeek = (realTaskDays / WORK_DAYS_PER_WEEK);
  sum += realTaskWeeks;
}

두 코드 중 확실히 아래 코드가 유지보수하기 수월할 것이다.

6. 인코딩을 피하라

인터페이스 클래스와 구현 클래스를 명명할 때, 인터페이스를 ShapeFactory라고 한다면 구현 클래스는 ShapeFactoryImpl 과 같이 작성하자.

사실 이 부분은 이해도 잘 안가고 와닿은 내용도 없었다.

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

독자가 코드를 읽으면서 변수 이름을 자신이 아는 이름으로 변환해야 한다면 그 변수 이름은 바람직하지 못하다.

똑똑한 프로그래머와 전문가 프로그래머 사이에서 나타나는 차이점 하나만 들자면, 전문가 프로그래머는 명료함이 최고라는 사실을 이해한다.
전문가 프로그래머는 자신의 능력을 좋은 방향으로 사용해 남들이 이해하는 코드를 내놓는다.

8. 클래스 이름

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

Customer, WikiPage, Account 등이 좋은 예다.
Manager, Processor, Data, Info 등과 같은 단어는 피하고, 동사는 사용하지 않는다.

Manager와 Processor는 왜 피해야하는지 솔직히 모르겠다

9. 메서드 이름

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

postPayment, deletePage, save 등이 좋은 예다.
접근자, 변경자, 조건자는 javabean 표준에 따라 값 앞에 get, set, is를 붙인다.

생성자를 overload할 때는 정적 팩토리 메서드(static factory method)를 사용하자.
메서드는 인수를 설명하는 이름으로 사용한다.

Complex fulcrumPount = new Complex(23.0);
vs
Complex fulcrumPount = Complex.FromRealNumber(23.0);

확실히 아래 코드는 위 코드에 비해 한 문장으로 맥락을 유추할 수 있다.

10. 기발한 이름은 피하라

당연한 소리!

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

추상적인 개념 하나에 단어 하나를 선택해 이를 고수하자.

예를 들어, 같은 메서드를 클래스마다 fetch, retrieve, get으로 제각각 부르면 혼란스럽다.
일관성을 유지하자.

12. 말장난을 하지 마라

11번과 연계되는 이야기이다.

예를 들어, 지금까지 구현한 add()는 기존 값 두 개를 더하거나 이어서 새로운 값을 만든다고 가정하자.
이때 새로 작성하는 메서드는 집합에 값 하나를 추가하는 메서드이다. 이때 똑같이 add라고 이름을 붙이는게 맞을까?

이때는 차라리 insert()로 이름을 짓자.

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

코드를 읽을 사람도 프로그래머라는 사실을 명심하자.
그러므로 전산용어, 알고리즘 이름, 패턴 이름, 수학 용어 등으로 사용해도 괜찮다.

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

적절한 '프로그래머 용어'가 없다면 문제 영역에서 이름을 가져오자.
그러면 코드를 보수하는 프로그래머가 분야 전문가에게 의미를 물어 파악할 수 있다.

15. 의미있는 맥락을 추가하라

예를 들어, firstName, lastName, street, houseNumber, city, state, zipcode라는 변수가 있다.
이렇게 훑어보면 주소라는 사실을 금방 알아챌 수 있다. 하지만 어느 메서드가 state 변수 하나만을 사용해야 한다면?
이때 state가 주소의 일부라는 사실을 금방 알아챌 수 있을까?

이때는 addr를 접두어로서 addrFirstName, addrLastName, addrState라 쓰면 의미가 분명해진다.
물론 Address라는 클래스를 생성하면 더 좋다.

// 1번 코드
private void printGuessStatistics(char candidate, int count) {
  String number;
  String verb;
  String pluralModifer;
  
  if(count == 0) {
    number = "no";
    verb = "are";
    pluralModifer = "s";
  } else if(count == 1) {
    number = "1";
    verb = "is";
    pluralModifer = "";
  } else {
    number = Integer.toString(count);
    verb = "are";
    pluralModifer = "s";
  }
  String guessMessage = String.format("There %s %s %s%s", verb, number, candidate, pluralModifer);
  print(guessMessage);
// 2번 코드
public class GuessStatisticsMessage {
	private String number;
	private String verb;
	private String pluraModifier;

	public String make(char candidate, int count) {
		createPluralDependentMessageParts(count);
		return String.format("There %s %s %s%s", verb, number, candidate, pluraModifier);
	}

	private void createPluralDependentMessageParts(int count) {
		if (count == 0) {
			thereAreNoLetters();
		} else if (count == 1) {
			thereIsOneLetter();
		} else {
			thereAreManyLetters(count);
		}
	}

	private void thereAreNoLetters() {
		number = "no";
		verb = "are";
		pluraModifier = "s";
	}

	private void thereIsOneLetter() {
		number = "1";
		verb = "is";
		pluraModifier = "";
	}

	private void thereAreManyLetters(int count) {
		number = Integer.toString(count);
		verb = "are";
		pluraModifier = "s";
	}
}

16. 불필요한 맥락을 없애라

마치면서

우리들 대다수는 자신이 짠 클래스 이름과 메서드 이름을 모두 암기하지 못한다.
암기는 요즘 나오는 도구에게 맡기고, 우리는 문장이나 문단처럼 읽히는 코드 아니면 적어도 표나 자료 구조처럼 읽히는 코드를 짜는 데만 집중해야 마땅하다.

profile
newBlog == https://kdkdhoho.github.io

0개의 댓글

관련 채용 정보