클린 코드 책 커버 #노마드코더 #북클럽 #노개북

요약 내용 (Notion)

TIL (Today I Learned)

변수, 함수, 인수, 클래스, 패키지, 소스 파일, 디렉터리 등등 소프트웨어의 다양한 부분에 이름이 붙는다. 이러한 이름을 잘 짓는 방법에 대해 공부했다.

오늘 읽은 범위

2장. 의미 있는 이름

책에서 기억하고 싶은 내용을 써보세요.

의도를 분명히 밝혀라


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

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<int[]> getFlaggedCells() {
    List<int[]> flaggedCells = new ArrayList<int[]>();
    for (int[] cell: gameBoard)
        if (cell[STATUS_VALUE] == FLAGGED)
            flaggedCells.add(cell);
    return flaggedCells;
}

이해하기 쉬운 코드

위 두 코드는 같은 동작을 하는 코드이지만 첫 번째 코드는 너무나 함축되어 있어 코드 맥락이 코드 자체에 명시적으로 드러나지 않는다.

위 코드가 지뢰찾기 게임이라고 할 때 다음과 같이 int 배열을 간단한 클래스로 만들어서 의미를 부여해도 좋다.

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

그릇된 정보를 피하라


  • 나름 널리 쓰이는 의미 있는 단어라도 다른 의미로 해석될 수 있는 경우 피한다.
  • 여러 계정을 그룹으로 묶을 때 실제 List가 아니라면 accountList와 같이 명명하지 않는다.
    • 실제 컨테이너가 List 형인 경웅라도 컨테이너 유형을 이름에 넣지 않는 것이 바람직하다.
  • 서로 흡사한 이름을 사용하지 않도록 주의한다.
  • 유사한 개념은 유사한 표기법을 사용한다. (일관성 유지)
  • 헷갈리는 이름을 쓰지 말아라.
    • l(소문자 L)이나 O(대문자 o) 등을 이름으로 사용하면 1이나 0과 헷갈린다.

의미 있게 구분하라


  • 컴파일러나 인터프리터만 통과하려고 연속된 숫자를 붙이거나 불용어를 추가하는 등의 방식은 적절치 않다.
  • 예를 들어 zork라는 변수가 있다고 해서 theZork라고 이름 지으면 안된다.
  • Product 클래스가 있다고 ProductInfo, ProductData 등으로 이름 짓는 경우도 마찬가지.
  • 읽는 사람이 차이를 알도록 이름을 지어라.

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


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

발음하기 어려운 이름

class Customer {
	private Date generationTimestamp;
	private Date modificationTimestamp;
	private final String recordId = "102";
}

발음하기 쉬운 이름

둘째 코드는 지적인 대화가 가능해진다.

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


  • 문자 하나를 사용하는 이름과 상수는 텍스트 코드에서 쉽게 눈에 띄지 않는다.
    • 예를 들어 ‘7’을 검색하면 파일 이름이나 수식이 모두 검색된다.
    • ‘e’와 같은 문자도 변수 이름으로 적합하지 못하다. 모든 프로그램, 거의 모든 문장에 등장할 것이다.
  • 한 문자로 나타내는 것은 간단한 메서드에서 로컬 변수 정도?
  • 이름 길이는 범위 크기에 비례해야 한다.

인코딩을 피하라


유형이나 범위 정보까지 인코딩에 넣으면 그만큼 이름을 해독하기 어려워진다.

  • 헝가리식 표기법
    • 변수의 타입을 이름에 포함하는 것
    • 과거 윈도 C API에서 주로 사용하던 표기법
      • 당시에는 컴파일러가 타입을 점검하지 않았기에 타입을 기억하기 위한 단서로 사용되었다.
    • 현재는 컴파일러가 타입을 기억하고 강제하기 때문에 자주 사용되지 않는다.
  • 멤버 변수 접두어
    • 멤버 변수에 m_이라는 접두어를 붙이는 것 -> 이제는 그럴 필요가 없다.
  • 인터페이스 클래스와 구현 클래스
    • 도형을 생성하는 ABSTRACT FACTORY를 구현한다고 할 때, 이 팩토리는 인터페이스 클래스다.
    • 구현은 구체 클래스(concrete class)에서 한다.
    • 옛날 코드에서는 인터페이스임을 알리기 위해 접두어 I를 붙였다.
    • 저자의 의견으로는 다음과 같은 이름이 IShapeFactory보다 좋다고 한다.
      • ShapeFactoryImp
      • CShapeFactory

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


  • 명료함이 최고다

클래스 이름


  • 클래스 이름과 객체 이름은 명사명사구가 적합하다.
    • Customer, WikiPage, Account, AddressParser 등 ⭕
    • Manager, Processor, Data, Info 등 ⚠️
    • 동사 ❌

메서드 이름


  • 메서드 이름은 동사동사구가 적합하다
    • postPayment, deletePage, save
  • 접근자, 변경자, 조건자는 javabean 표준에 따라 값 앞에 get, set, is를 붙인다.

    string name = employee.getName();
    customer.setName("mike");
    if (paycheck.isPosted())...
    
  • 생성자를 중복 정의(overload) 할 때는 정적 팩토리 메서드를 사용한다.
  • 메서드는 인수를 설명하는 이름을 사용한다.
    Complex fulcrumPoint = Complex.FromRealNumber(23.0);
    

    위 코드보다 아래 코드가 좋다.

    Complex fulcrumPoint = new Complex(23.0);
    

기발한 이름은 피하라


특정 문화에서만 사용하는 농담은 피하고, 의도를 분명하고 솔직하게 표현하라.

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


  • 추상적인 개념 하나에 단어 하나를 선택해 이를 고수한다
    • 똑같은 메서드를 클래스마다 fetch, retrieve, get으로 제각각 부르면 혼란스럽다

말장난을 하지 마라


  • 한 단어를 두 가지 목적으로 사용하지 마라.
    • 지금까지 구현한 add 메서드가 기존 값 두 개를 더하거나 이어서 새로운 값을 만든다고 하자
    • 새로 작성하는 메서드는 집합에 값 하나를 추가한다고 할 때 이 메서드를 add라 지어도 괜찮을까?
      • 이 메서드는 기존의 add 메서드와 맥락이 다르다. 그러므로 insertappend라는 이름이 적당하다.
  • 다른 개념에 같은 단어를 사용한다면 말장난에 불과하다.

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


  • 코드를 읽는 사람도 프로그래머다
  • 전산 용어, 알고리즘 이름, 패턴 이름, 수학 용어 등을 사용해도 좋다.
  • 모든 이름을 문제 영역(domain)에서 가져오는 정책은 현명하지 못하다.

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


  • 적절한 프로그래머 용어가 없다면 문제 영역에서 이름을 가져온다.
  • 그러면 전문가에게 의미를 물어 파악할 수 있다.

의미 있는 맥락을 추가하라


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

  • 예를 들어 firstName, lastName, street, houseNumber, city, state, zipcode 라는 변수가 있을 때 변수를 훑어보면 주소라는 사실을 알 수 있다.
  • 하지만 state 변수 하나만 사용된다면 state가 주소 일부라는 사실을 알기 힘들다
  • 이때 addr라는 접두어를 추가해 addrFirstName, addrLastName, addrState라 쓰면 맥락이 분명해진다.
  • Address 라는 클래스를 생성하면 더 좋다.

불필요한 맥락을 없애라


  • IDE는 개발자를 지원하는 도구다. IDE를 방해할 이유가 없다.
  • 일반적으로 짧은 이름이 긴 이름보다 좋지만 이는 의미가 분명한 경우에 한해서다.

마무리


  • 좋은 이름을 선택하려면 설명 능력이 뛰어나야 하고 문화적 배경이 같아야 한다.
  • 암기는 요즘 나오는 도구에게 맡기고, 우리는 표나 자료 구조처럼 읽히는 코드를 짜는 데만 집중해야 한다.
  • 코드를 개선하려는 노력을 중단해서는 안 된다.

오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요

저자의 말대로 소프트웨어에서 변수, 함수, 인수, 클래스, 패키지, 소스 파일, 디렉터리 등등 다양한 부분에 이름을 붙여야 한다. 프로그래밍 언어를 처음 배울 때 우스갯 소리로 들었던 것이 코딩보다 이름 짓는 것이 더 어렵다는 말이었다.

그만큼 적절한 이름을 짓는 것은 쉬운 일이 아니다.

내가 평소에 코드를 짜며 이름을 지을 때는 보통 내가 학습한 자료나 강의 등에서 이름 붙이는 방식을 따라 갔던 것 같다. 클래스와 객체 이름은 명사로 짓고, 메서드 이름은 동사로 짓는 등의 널리 알려진 방식에서는 잘 따르고 있었지만 인터페이스에 ‘I’ 접두사를 붙인다거나 nameTextView와 같이 타입을 붙이는 등의 작명 법은 나도 그렇고 아직 다른 사람들도 많이 사용하는 방법이라서 무조건 적으로 이 책의 내용을 따를 것이 아니라 함께 개발하는 사람들과 맞춰 바꿔 나가는 방향으로 하면 좋을 것 같다.

궁금한 내용이 있거나, 잘 이해되지 않는 내용이 있다면 적어보세요.

정적 팩토리 메서드: 객체 생성 역할을 하는 클래스 메서드

// LocalTime.class
...
public static LocalTime of(int hour, int minute) {
    ChronoField.HOUR_OF_DAY.checkValidValue((long)hour);
    if (minute == 0) {
        return HOURS[hour];
    } else {
        ChronoField.MINUTE_OF_HOUR.checkValidValue((long)minute);
        return new LocalTime(hour, minute, 0, 0);
    }
}
...

// hour, minutes을 인자로 받아서 9시 30분을 의미하는 LocalTime 객체를 반환한다.
LocalTime openTime = LocalTime.of(9, 30); 

위 코드에서 LocalTime 클래스의 of 메서드처럼 직접적으로 생성자를 통해 객체를 생성하는 것이 아닌 메서드를 통해서 객체를 생성하는 것을 정적 팩토리 메서드라고 한다.

public enum Color {
    RED,
    BLUE;
}
...
Color redColor = Color.valueOf("RED");
Color blueColor = Color.valueOf("BLUE");

위 코드처럼 enum 요소를 조회할 때 사용하는 valueOf도 정적 팩토리 메서드의 일종이라고 할 수 있다.