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

yourjin·2022년 2월 27일
0

read.log

목록 보기
34/37
post-thumbnail

TIL (2022.02.24)

DAY 11

🔖 오늘 읽은 범위 : 15장, JUnit 들여다보기


😃 **책에서 기억하고 싶은 내용을 써보세요.**
  • JUnit 프레임워크

    • 위 테스트 케이스로 ComparisonCompactor 모듈에 대한 코드 커버리지 분석을 수행했더니 100%가 나왔다. 테스트 케이스가 모든 행, 모든 if 문, 모든 for 문을 실행한다는 의미다. 그래서 나는 모듈이 올바로 동작한다고 자신하게 되었고 모듈 작성자들의 장인정신을 높이 사게 되었다.

    • 디펙터링은 리펙터링의 반대 과정이다. 디펙터링 결과로 나온 코드는 구조적으로 어지럽고 취약하다.

    • ComparisonCompactor.java(원본)

      package junit.framework;
       public class ComparisonCompactor {
       		private static final String ELLIPSIS = " … II ;
       		private static final String DELTA_END = " ] " ;
       		private static final String DELTA_START = " [" ;
       		private int fContextlength ;
       		private String fExpected;
       		private String fActual;
       		private int fPrefix;
       		private int fSuffix;
       
       		public ComparisonCompactor(int contextlength,
       															 String expected,
       															 String actual) {
       				fContextLength = contextlength;
       				fExpected = expected;
       				fActual == actual;
       		}
       
       		public string compact(String message) {
       				if (fExpected = null || fActual = null || areStringsEqual())
       					 return Assert.format(message, fExpected, fActual) ; 
       				findCommonPrefix( ) ;
       				findCommonSuffix( ) ;
       				String expected = compactString(fExpected);
       				String actual = compactString(fActual);
       				return Assert.format(message, expected, actual) ; 
       		}
       
       		private String compactString(String source) {
       				String result = DELTA_START + source.substring(fPrefix, source.length() - fSuffix + 1) + DELTA_END;
       				if (fPrefix > 0)
       						result = computeCommonPrefix() + result;
       				if (fSuffix > 0)
       						result = result + computeCommonSuffix();
       				return result ;
       		}
       
       		private void findCommonPrefix( ) {
       				fPrefix = 0;
       				int end = Math.min(fExpected.length(), fActual.length());
       				for ( ; fPrefix < end; fPrefix++) {
       						if (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix))
       						break;
       				}
       		}
       
       		private void findCommon5uffix() {
       				int expectedSuffix = fExpected.length() - 1;
       				int actualSuffix = fActual.length ( ) - 1 ;
       				for ( ;
       							actualSuffix >= fPrefix && expectedSuffix >= fPrefix;
       							actualSuffix--, expectedSuffix-- ) {
       						if ( fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix)) l
       							break;
       				}
       				fSuffix = fExpected.length() - expectedSuffix;
       		}
       
       		private String computeConmonPrefix( ) {
       				return (fPrefix > fContextLength ? ELLIPSIS : "") + fExpected.substring(Math.max(0, fPrefix - fContextLength), fPrefix);
       		}
       
       		private String computeCommonSuffix( ) {
       				int end == Math.min(fExpected.length() - fSuffix + 1 + fContextLength, fExpected.length( )) ; 
       				return fExpected.substring(fExpected.length() - fSuffix + 1, end) +
       							 (fExpected.length( ) - fSuffix + 1 < fExpected.length( ) - fContextLength ? ELLIPSIS : "") ;
       		}
       
       		private boolean areStringsEqual() {
       				return fExpected.equals(fActual) ;
       		}
       }
    • 참고 링크: junit4/ComparisonCompactor.java at main · junit-team/junit4

    • 비록 저자들이 모듈을 아주 좋은 상태로 남겨두었지만 보이스카우트 규칙에 따르면 우리는 처음 왔을 때보다 더 깨끗하게 해놓고 나가야 한다. 그렇다면 원래 코드를 어떻게 개선하면 좋을까?

      • 맴버 변수 앞에 붙인 접두어 f
        • 오늘날 사용하는 개발 환경에서는 이처럼 변수 이름에 범위를 명시할 필요가 없다. 접두어 f는 중복되는 정보다. 그러므로 접두어 f를 모두 제거하자.
      • 캡슐화하지 않은 조건문
        • 의도를 명확히 표현하려면 조건문을 캡슐화해야 한다. 즉, 조건문을 메서드로 뽑아내 적절한 이름을 붙인다.
      • 함수에서 맴버 변수와 이름이 똑같은 변수를 사용하는 이유가 무엇일까? 서로 다른 의미가 아닌가? 이름은 명확하게 붙인다.
      • 부정문은 긍정문보다 이해하기 약간 더 어렵다. 그러므로 첫 문장 if를 긍정으로 만들어 조건을 반전한다.
      • 함수 이름에 숨겨진 의미는 없는지, 인수와 반환 값은 어떤 값인지 명확하게 표현하도록 수정한다. 새 이름에 인수를 고려하면 가독성이 훨씬 더 좋아진다.
      • 여러 작업을 하는 함수가 있다면, 함수를 분리해 특정 한 가지 책임만 할 수 있게 한다.
      • 어떤 함수는 반환 값이 있지만 어떤 함수는 반환 값이 없다면, 함수 사용방식이 일관적이지 못하다. 한가지 방식으로 통일해주자
      • findCommonSuffix를 주의 깊게 살펴보면 숨겨진 시간적인 결합(hidden temporal coupling) 이 존재한다. 다시 말해, findCommandSuffixfindCommonPrefix가 prefixlndex를 계산한다는 사실에 의존한다. 만약 findCommonPrefixfindCommonSuffix를 잘못된 순서로 호출하면 밤샘 디버깅이라는 고생문이 열린다. 그래서 시간 결합을 외부에 노출하고자 findCommonSuffix를 고쳐 prefixlndex 를 인수로 넘겼다.
        prefixIndex = findCommonPrefix();
        suffixIndex = findCommonSuffix(prefixlndex);
                                                                                 
                         

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

  • 오늘은 JUnit 프레임워크의 ComparisonCompactor 클래스를 살펴보고, 이를 더 개선하는 작업을 해보았다. 일단 원본 코드 자체도 워낙 깔끔하게 익히는 편이라, 이 코드의 기초가 비행기에서 완성되었다는 사실이 놀라웠다. 그리고 개선해 나가는 과정에서 여태까지 배운 기법들을 총 동원한 것 같아, 복습이 되어 좋았다.
  • 한 가지 아쉬운 점은 나는 이번 챕터에서 JUnit 프레임워크가 어떤 것이며, 이를 잘 활용한 테스트 코드를 짜는 방법을 배울 수 있을 것이라고 생각했는데, 그 보다는 이전 챕터(14장. 점진적인 개선)의 연장선으로 느껴졌다. JUnit에 관해서는 따로 공부하는 것이 좋을 듯하다.

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

소감 3줄 요약

  • 이미 좋은 모듈이라고 여겨져도, 보이스카우트 규칙에 따라 우리는 처음 왔을 때보다 더 깨끗하게 해놓고 나가야 한다.
  • 의도를 명확히 표현하려면 조건문을 캡슐화해야 한다. 즉, 조건문을 메서드로 뽑아내 적절한 이름을 붙인다.
  • 반환 값과 같이 함수의 사용 방식에 영향을 끼치는 것은 일관되게 나올 수 잇도록 한 가지 방식으로 통일한다.
profile
make it mine, make it yours

0개의 댓글