의미있는 이름

깨끗한 코드란 무엇일까

책너두 5기 3일차

로버트 C.마틴의 클린코드 p.13 ~ p.23

내용 정리

  • 론 제프리스

  1. 모든 테스트를 통과한다.
  2. 중복이 없다.
  3. 시스템 내 모든 설계 아이디어를 표현한다.
  4. 클래스, 메서드, 함수 등을 최대한 줄인다.
  5. 중복을 줄이고 한 기능만 수행하며 표현력을 높여라. 작게 추상화하라.
  • 워드 커닝햄

  1. 코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드다.
  2. 코드가 그 문제를 풀기 위한 언어처럼 보인다면 아름다운 코드다.
  3. 코드를 독해하느라 고민해야할 필요가 없어야 한다.
  4. 언어를 단순하게 보이도록 만드는 책임은 우리에게 있다.

저자의 생각

이 책은 저자와 동료들이 옳다고 믿고 있는 깨끗한 코드에 대해 설명한다. 무조건 진리이니 받아들인다는 자세는 곤란하지만 고민하고 생각하고 공부하면 좋은 결과를 낼 수 있을 것이다. 또한 이 책과 다른 의견도 마땅히 수용해야 한다.

우리는 저자다

코드를 작성할 때 자신이 저자라는 사실과 자신의 노력을 보고 판단을 내릴 독자가 있다는 사실을 기억해야 한다. 흔히 코드를 읽는 시간보다 작성하는 시간이 훨씬 길다고 생각한다. 하지만 새로운 코드를 짜는 것의 대부분도 기존의 코드를 끊임없이 읽는 것이다. 따라서 읽기 쉬운 코드를 짜는 것이 쉽진 않아도 그래야만 새 코드를 짜는 것이 수월해진다.

보이스카우트 규칙

캠프장은 처음 왔을 때보다 더 깨끗하게 해놓고 떠나라.

한꺼번에 많은 시간과 노력을 투자해 코드를 정리할 필요가 없다. 변수 이름 하나를 개선하고, 조금 긴 함수 하나를 분할하고, 약가느이 중복을 제거하고, 복잡한 if 문 하나를 정리하면 충분하다.

프리퀄과 원칙

이 책은 Agile Software Development: Principles, Patterns, and Practices(PPP)의 프리퀄이다. PPP에서 제안하는 객체 지향 설계의 다섯 가지 원칙은 다음과 같다.

  • SRP(The Single Responsibility Principle): 클래스에는 한 가지, 단 한 가지 변경 이유만 존재해야 한다.
  • OCP(The Open Closed Principle): 클래스는 확장에 열려 있어야 하며 변경에 닫혀 있어야 한다.
  • LSP(The Liskov Substitution Principle): 상속받은 클래스는 기초 클래스를 대체할 수 있어야 한다.
  • DIP(The Dependency Inversion Principle): 추상화에 의존해야 하며, 구체화에 의존하면 안된다.
  • ISP(The Interface Segregation Principle): 클라이언트에 밀접하게 작게 쪼개진 인터페이스를 유지한다.

결론

이 책을 읽ㄷ는다고 뛰어난 프로그래머가 된다는 보장은 없다. 뛰어난 프로그래머가 생각하는 방식과 사용하는 기술과 기교와 도구를 소개한다. 연습해야 한다.

의미있는 이름

들어가면서

소프트웨어에서 이름은 어디에나 쓰인데. 변수, 함수, 인수, 클래스, 패키치 등등 모든 곳에서. 이름을 잘 짓는 규칙이 있다.

의도를 분명히 밝혀라

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

예시1)

int d; // 경과 시간(단위: 날짜)

이 코드에서 이름 d는 아무 의미도 없다.

1
2
3
4
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;

이 이름들에는 의도가 드러난다.

예시2)

1
2
3
4
5
6
7
public List<int[]> getThem() {
    List<int[]> list1 = new ArrayList<int[]>();
    for (int[] x: theList)
        if (x[0] == 4)
            list1.add(x);
    return list1;
}

이 코드는 단순하지만 하는 일을 짐작하기 어렵다. 함축성이 원인이다. 위 코드는 독자가 아래 정보를 알고 있다고 가정하고 있다.

  1. theList에 무엇이 들었는가?
  2. theList에서 0번째 값이 어째서 중요한가?
  3. 값 4는 무슨 의미인가?
  4. 함수가 반환하는 리스트 list1을 어떻게 사용하는가?

정보 제공은 충분히 가능했었다. 이 코드가 지뢰찾기 게임이었다면 theList가 게임판이기 때문에 gameBoard로 바꿀 수 있다. 배열의 0번째 값은 칸 상태를 뜻한다. 값 4는 깃발이 꽂힌 상태를 가리킨다. 이를 바탕으로 다음과 같이 수정할 수 있다.

1
2
3
4
5
6
7
public List<int[]> getFlaggedCells() {
    List<int[]> flaggedCells = new ArrayList<int[]>();
    for (int[] cell : gameBoard)
        if (cell[STATUS_VALUE] == FALGGED)
            flaggedCells.add(cell);
    return flaggedCells;
}

단순성은 유지하되, 코드가 명확해졌다. 더 나아가면 int배열을 사용하는 대신, 칸을 간단한 클래스로 만들고 isFlagged라는 좀 더 명시적인 함수를 사용해 FLAGGED라는 함수를 감출 수도 있다.

1
2
3
4
5
6
7
public List<Cell> getFlaggedCells() {
    List<cell> flaggedCells = new ArrayList<Cell>();
    for (Cell cell : gameBoard)
        if (cell.isFlagged())
            flaggedCells.add(cell);
    return flaggedCells;
}

좋은 이름을 통해 함수가 하는 일을 이해하기 쉬워졌다.


책을 읽고

사실 알고리즘을 풀 때는 최대한 빠르고 간결하게 푸느라 수학 방정식을 풀 듯 간단한 이름을 작성하였다. 프로젝트를 할 때도 그 버릇이 남아있어서 인지 최대한 짧게 명명하려고 노력했다. 긴 단어를 쓰는게 복잡하고 오히려 멋없다고 생각했던것 같다. 내 코드를 남이 편하게 읽을 수 있도록, 내일 내가 다시 읽어도 해석을 위해 시간이 필요하지 않도록 이름을 명확하게 지어야겠다.

알게된 것

함축성

내가 코드를 작성할 때, 당연히 알고 있는 것을 독자도 알고 있다고 가정해선 안된다. 각주로 부연설명이 필요한 코드는 안좋은 코드이다.