[클린코드 완독스터디] TIL (2022.02.26)

yourjin·2022년 3월 2일
0

read.log

목록 보기
36/37
post-thumbnail

TIL (2022.02.26)

DAY 13

🔖 오늘 읽은 범위 : 16장. SerialDate 리펙터링


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

  • SerialDate는 날짜를 표현하는 자바 클래스다. 하지만 자바는 이미 java.util.Date, java.util.Calendar 등과 같은 클래스를 제공한다. SerialDate 클래스가 왜 필요할까? 데이비드는 나 역시 자주 느꼈던 불편함을 없애고자, SerialDate 클래스를 구현했다. Javadoc 주석에 데이비드는 이유를 잘 설명한다.
    /**
     *  An abstract class that defines our requirements for manipulating dates,
     *  without tying down a particular implementation.
     *  <P>
     *  Requirement 1 : match at least what Excel does for dates;
     *  Requirement 2 : the date represented by the class is immutable;
     *  <P>
     *  Why not just use java.util.Date?  We will, when it makes sense.  At times,
     *  java.util.Date can be *too* precise - it represents an instant in time,
     *  accurate to 1/1000th of a second (with the date itself depending on the
     *  time-zone).  Sometimes we just want to represent a particular day (e.g. 21
     *  January 2015) without concerning ourselves about the time of day, or the
     *  time-zone, or anything else.  That's what we've defined SerialDate for.
     *  <P>
     *  You can call getInstance() to get a concrete subclass of SerialDate,
     *  without worrying about the exact implementation.
     *
     * @author David Gilbert
     */
  • 첫째, 돌려보자
    • SerialDateTests라는 클래스는 단위 테스트 케이스 몇 개를 포함한다. 돌려보면 실패하는 테스트 케이스는 없다. 하지만 테스트 케이스를 훑어보면 모든 경우를 점검하지 않는다는 사실이 드러난다.
    • 그래서 나는 (코드 커버리지 분석 도구인) 클로버(Colver)를 이용해 단위 테스트가 실행하는 코드와 실행하지 않는 코드를 조사했다. 클로버에 따르면, SerialDate에서 실행 가능한 문장 185개 중 단위 테스트가 실행하는 문장은 91개에 불과했다. 즉, 대략 50% 정도였다.
    • 2004년 12월 25일은 토요일이었다. 다음 토요일은 2005년 1월 1일이다. 하지만 테스트를 돌리면 getFollowingdayOfWeek 메서드가 12월 25일을 다음 토요일로 반환한다. 명백한 버그다. 전형적인 경계 조건 오류다.
    • 클로버로 확인한 테스트 커버리지 패턴도 흥미롭다. 464쪽 719행은 결코 실행되지 않는다. 즉, 718행에 있는 if 문이 항상 거짓이라는 말이다. (...) 즉, 알고리즘이 틀렸다.
  • 둘째, 고쳐보자
    • 449쪽 67행부터 나오는 Javadoc 주석은 HTML 태그를 사용한다. 한 소스 코드에 여러 언어를 사용하다니, 나로서는 탐탁치 못하다. 이 주석은 네 가지 언어(자바, 영어, Javadoc, HTML)를 사용한다. 언어를 네 개나 사용하다 보니 모양새를 제대로 맞추기 어렵다.
    • 클래스 이름이 SerialDate인 이유는 ‘일련번호(serial number)를 사용해 클래스를 구현했기 때문이다. 여기서는 1899년 12월 30일을 기준으로 경과한 날짜 수를 사용한다.
    • 나로써는 두 가지가 꺼림칙하다.
      • 첫째, ‘일련번호’라는 용어는 정확하지 못하다. 일련번호보다 ‘상대 오프셋(relative offset)’이 더 정확하다. ‘일련번호’라는 용어는 날짜보다 제품 식별 번호로 더 적합하다.
      • 둘째로 꺼림직한 문제는 좀 더 중요하다. SerialDate라는 이름은 구현을 암시하는데 실상은 추상 클래스다. (...) 그래서 나는 SerialDate라는 이름의 추상화 수준이 올바르지 못하고 생각한다.
      • 여러모로 고민한 끝에 나는 DayDate를 쓰기로 결정했다.
    • MonthConstants를 상속하는 이유는 무엇일까?
      • 상수 클래스를 상속하면 MonthConstants.January와 같은 표현을 사용할 필요가 없어진다. 옛날 자바 프로그래머들이 많이 쓰던 기교인데, 바람직하다고 보기는 어렵다. MothConstants는 enum으로 정의해야 마땅하다.
      • 달(月)을 int로 받던 메서드는 이제 Month로 받는다. 즉, isValidMonthCode 메서드를 없애도 된다는 뜻이다. 또한, monthCodeToQuarter와 같은 오류 검사 코드도 더 이상 필요하지 않다.
    • serialVersionUID
      • serialVersionUID는 변수 직렬화(serializer)를 제어한다. 이 변수 값을 변경하면 이전 소프트웨어에서 직렬화한 DayDate를 더 이상 인식하지 못한다. 즉, 이전 버전에서 직렬화한 DayDate 클래스를 복원하려 시도하면 InvalidClassException이 발생한다.
      • serialVersionUID 변수를 선언하지 않으면 컴파일러가 자동으로 생성한다. 즉, 모듈을 변경할 때마다 변수 값이 달라진다. (...) 나로서는 자동 제어가 훨씬 더 안전하게 여겨 진다.
      • 이 책을 검토한 검토자 여러 명이 내 의견에 반론을 제기했다. 그들은 오픈 소스 프레임워크에서는 직렬화 ID를 직접 제어하는 편이 낫다고 주장했다. 사소한 변경을 가했을 뿐인데 이전 클래스를 복원하지 못하는 사태를 피하기 위해서다.
      • 하지만 자동 제어를 사용함으로써 발생하는 오류는 (개발자 입장에서 불편할지라도) 원인과 결과가 분명하다. 반면, 클래스 작성자가 깜박하고 ID를 갱신하지 않아 발생하는 오류는 원인도 결과도 모호하다.
    • 불필요한 주석은 거짓말과 잘못된 정보가 쌓이기 좋은 곳이다.
    • DayDate는 어디까지나 추상 클래스로, 구체적인 구현 정보를 포함할 필요가 없다. (...) 즉, 추상 클래스 사용자가 구현 정보를 알아야 한다는 딜레마가 생긴다.
    • 일반적으로 기반 클래스(base class, 부모 클래스)는 파생 클래스(derivative,자식 클래스)를 몰라야 바람직하다. 그래서 ABSTRACT FACTORY 패턴을 적용해 DayDateFactory 를 만들었다.
    • 또한 인수와 변수 선언에서 final 키워드를 모두 없앴다. 실질적인 가치는 없으면서 코드만 복잡하게 만든다고 판단했기 때문이다. final을 제거하겠다는 결정은 일부 기존 관례에 어긋난다. 예를 들어, 로버트 시몬스는 “코드 전체에 final을 사용하라...” 고 강력히 권장한다.
    • 일반적으로 메서드 인수로 플래그는 바람직하지 못하다. 특히 출력 형식을 선택하는 플래그는 가급적 피하는 편이 좋다.
    • 461쪽 562행에서 576행까지 addDays 메서드가 나온다. 우선, 이 메서드는 온갖 DayDate 변수를 사용하므로 static이어서는 안된다. 그래서 인스턴스 메서드로 변경했다.

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

  • 오늘도 리펙터링을 통해 보이스카우트 규칙을 따르는 모습을 보았다. 여러 번 반복해서 보다 보니 이쯤되면 이 부분을 고칠 것 같다는 생각이 들기도 했다. 선배 개발자들이 먼저 발견한 깨끗한 코드를 작성하는 패턴을 이런 식으로 배울 수 있다는 점에 감사하다. 나 같은 초보 개발자에게는 좋은 코드를 보는 것만으로도 많은 도움이 된다. 이 패턴이 언제까지고 맞는 방법이라고 장담할 수는 없지만, 적어도 내가 이 과정을 기억하는 한 의미가 있을 것이라고 생각한다.

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

  • 변수 직렬화(serializer)
  • 기반 클래스(base class, 부모 클래스)와 파생 클래스(derivative,자식 클래스)

소감 3줄 요약

  • 자주 느꼈던 불편함을 해소하기 위해 별도의 클래스를 만들고, 이를 다시 리펙터링하는 과정을 보며 보이스카우트 규칙으로 얼마나 개선될 수 있는지를 다시 한번 느낀다.
  • 불필요한 주석은 거짓말과 잘못된 정보가 쌓이기 좋은 곳이다.
  • 일반적으로 기반 클래스(base class, 부모 클래스)는 파생 클래스(derivative,자식 클래스)를 몰라야 바람직하다.
profile
make it mine, make it yours

0개의 댓글