티스토리 뷰

15장 JUnit 들여다 보기

- 책에서 소개해주는 모듈은 문자열 비교 오류를 파악할 때 유용한 코드

ex) ABCDE와 ABXDE를 받아 <...B[X]D...>로 반환

- 소스 흉내내면서 책에서 하라는대로 같이 수정해봤는데 되게 재미있었다

 

최종 ComparsionCompactor.java

public class ComparisonCompactor {
    private static final String ELLIPSIS="...";
    private static final String DELTA_END="]";
    private static final String DELTA_START="[";

    private int contextLength;
    private String expected;
    private String actual;
    private int prefixLength;
    private int suffixLength;

    private String compactExpected;
    private String compactActual;

    public ComparisonCompactor(int contextLength,String expected, String actual){
        this.contextLength = contextLength;
        this.expected = expected;
        this.actual = actual;
    }

    public String formatCompactedComparison(String message){
        String compactExpected = expected;
        String compactActual =actual;
        if(shouldBeCompacted()){ //조건문을 메서드로 뽑아냄
            findCommPrefixAndSuffix();
            compactExpected = compact(expected);
            compactActual = compact(actual);
        }
        return Assert.format(message, compactExpected, compactActual);
    }

    private boolean shouldBeCompacted(){ // 매서드 명은 긍정으로
        return !shouldNotBeCompacted();
    }
    
     private boolean shouldNotBeCompacted(){
        return expected == null || actual == null || expected.equals(actual);
    }
    
    private void  findCommPrefixAndSuffix(){
        findCommPrefix();
        suffixLength = 0;
        for(; !suffixOverlapsPrefix();suffixLength++ ) {
            if(charFromEnd(expected, suffixLength) != charFromEnd(actual, suffixLength))
                break;
        }
    }

    private char charFromEnd(String s, int i){
        return s.charAt(s.length()-i-1);
    }

    private boolean suffixOverlapsPrefix(){
        return actual.length() - suffixLength <= prefixLength ||
                expected.length() - suffixLength <= prefixLength;
    }

    private void findCommPrefix(){
        prefixLength = 0;
        int end = Math.min(expected.length(), actual.length());
        for(; prefixLength<end; prefixLength++){
            if(expected.charAt(prefixLength) != actual.charAt(prefixLength))
                break;
        }
    }


    private String compact(String s){
        return new StringBuilder()
                .append(startingEllipsis())
                .append(startingContext())
                .append(DELTA_START)
                .append(delta(s))
                .append(DELTA_END)
                .append(endingContext())
                .append(endingEllipsis())
                .toString();
    }

    private String startingEllipsis(){
        return prefixLength > contextLength ? ELLIPSIS : "";
    }

    private String startingContext(){
        int contextStart = Math.max(0, prefixLength -contextLength);
        int contextEnd = prefixLength;
        return expected.substring(contextStart, contextEnd);
    }

    private String delta(String s){
        int deltaStart = prefixLength;
        int deltaEnd = s.length() - suffixLength;
        return s.substring(deltaStart, deltaEnd);
    }

    private  String endingContext(){
        int contextStart = expected.length() - suffixLength;
        int contextEnd = Math.min(contextStart+contextLength, expected.length());
        return expected.substring(contextStart, contextEnd);
    }

    private  String endingEllipsis(){
        return (suffixLength > contextLength ? ELLIPSIS : "");
    }
}

- 변수명에 범위를 명시할 필요가 없다 (ex. fContextLength ->contextLength )

- 조건문은 메서드로 뽑아내자(의도를 명확하게 표현하려면 조건문을 캡슐화 해야한다)

- 함수명은 최대한 긍정문으로(부정문은 이해하기 어렵다)

- 불필요한 if문 제거하고 구조를 다듬기

- 전체 함수는 함수가 사용된 직후에 정의된다. 분석 함수가 먼저 나오고 조합 함수가 그 뒤를 이어서 나온다

- 보이스카우트 규칙 - 캠프장은 처음 왔을 때보다 더 깨끗하게 해놓고 떠나라

한꺼번에 많은 시간과 노력을 투자해 코드를 정리할 필요가 없다.

변수 이름 하나를 개선하고, 조금 긴 함수 하나를 분할하고, 약간의 중복을 제거하고, 복잡한 if문 하나를 정리하면 충분하다

 

 

 

 

 

16장 SerialDate 리팩터링

- SerialDate라는 날짜를표현하는 자바 클래스를 리팩터링하는 내용

- 부록이랑 같이 왔다갔다 하면서 본다고 불편하긴 했는데 이것도 재미있엇음

 

1. 둘러보자

- 클로버를 이용해봤을때 SerialDate는 테스트 커버리지가 50%정도 였다

이를 통해 단위 테스트가 실행되지 않은 코드가 클래스 여기저기에 흩어져 있는걸 파악하고 테스트 케이스를 구현했다

 

2. 고쳐보자

- 라이센스 정보, 저작권은 피룡하지만 변경이력같은 내용은 필요없기때문에 지운다

- import문은 java.text.* 와 java.util.*로 줄여도 된다

- Enum을 활용한 정적 변수들을 활용한다

- 메서드 명을 서술적이고 정확하게 사용한다

- 중복이 있으면 새 메서드를 생숭해 중복을 없앤다

 

 

 

 

 

17장. 냄새와 휴리스틱

- 앞에서 했던 얘기들을 요약한 느낌? 기억하고 싶은 내용들 몇개만 다시 정리해본다

 

주석

- 부적절한 정보 : 작성자, 최종 수정일, SPR 번호 등과 같은 메타 정보만 주석으로 넣는다

- 쓸모없는 주석 : 오래된 주석, 엉뚱한 주석 잘못된 주석은 쓸모가 없다. 이런 주석은 재빨리 삭제한다

- 성의없는주석 : 주석을 달 참이라면 시간을 들여 최대한 멋지게 작성한다. 주절대지 않고 당연한 소리를 반복하지 않는다. 간결하고 명료하게 작성한다

- 주석 처리된 코드는 아무도 삭제하지 않는다. 누군가에게 필요하거나 다른 사람이 사용할 코드라 생각하기 때문이다.

주석으로 처리된 코드는 삭제하라

 

환경

- 여러 단계로 빌드해야한다

- 여러 단계로 테스트해야 한다

 

함수

- 함수에서 인수 개수는 작을수록 좋다

- 함수에서 뭔가의 상태를 변경해야 한다면 출력 인수를 쓰지말고, 함수가 속한 객체의 상태를 변경한다

- boolean 인수는 함수가 여러 기능을 수행한다는 명백한 증거다. 프래그 인수는 혼란을 초래하므로 피해야 마땅하다.

- 아무도 호출하지 않는 함수는 삭제한다. 죽은 코드는 낭비다

 

일반

- 상적으로는 소스 파일 하나에 언어 하나만 사용하는 방식이 가장 좋지만 현실적으로는 불가피하다. 소스 파일에서 언어 수와 범위를 최대한 줄이도록 애써야 한다

- 자신의 직관에 의존하지 말고 모든 경계의 조건을 찾아내고 모든 조건을 테스트 하는 테스트 케이스를 작성하라

- 컴파일러 경고 일부를 꺼버리면 빌드가 쉬워질지 모르지만, 자칫하면 끝없는 디버깅에 시달린다

- 중복된 코드를 하위루틴이나 다른 클래스로 분리하라

- 똑같은 코드가 여러차례 나오는 중복은 함수로 교체한다

- switch/case나 if/esle문으로 똑같은 조건을 겁듭 확인하는 중복은 다향성(polymorphism)으로 대체한다

- 알고리즘이 유사하나 코드가 서로 다른 중복은 TEMPLATE METHOD 패턴이나 STRATEGY 패턴으로 중복을 제거한다

- 수직 분리 : 변수와 함수는 사용되는 위치에 가깝게 정의한다. 선언한 위치로부터 몇백 줄 아래에서 사용하면 안된다

- 어떤 개념을 특정 방식으로 구현했다면 유사한 개념도 같은 방식으로 구현해야 한다

- 비어있는 생성자, 사용하지 않는 변수, 호출하는 않는 함수 등등 모두 삭제

- 인위적 결합 - 서로 무관한 개념을 인위적으로 결합하지 않는다

ex) 일반적인 enum은 특정 클래스에 속할 이유가 없다

- 부정 조건은 피하라

ex.

if(!buffer.shouldNotCompact()) 보다

if(buffer.shouldCompact()) 같이 쓰기

 

- 함수는 한 가지만 해야한다

 

 

자바

- import목록을 피하고 와일드카드를 사용하라

- 상수 대 Enum

public class HourlyEmployee extends Employee {
    private int tenthsWorked;
    HourlyPayGrade grade;
    
    public Money calculatePay(){
        int straightTime = Math.min(tenthsWorked, TENTHS_PER_WEEK);
        int overTime = tenthsWorked - straightTime;
        return new Money(
             grade.rate() * (tenthsWorked + OVERTIME_RATE * overTime)
        );
    } 
    
    .......
    
   public enum HourlyPayGrade{
       APPRENTICEP{
           public double rate(){
               return 1.0;
           }
       },
       LIEUTENANT_JOURNEYMAN{
           public double rate(){
               return 1.2;
           }
       },
       JOURNEYMAN{
           public double rate(){
               return 1.5;
           }
       },
       MASTER{
           public double rate(){
               return 2.0;
           }
       };
       
       public abstract double rate();
   }   
}

 

 

이름

- 서술적인 이름을 사용하라. 소프트웨어 가독성의 90%는 이름이 결정한다

- 긴 범위는 긴 이름을 사용하라. 이름 길이는 범위 길이에 비례해야한다

 

테스트

- 사소한 테스트를 건너뛰지 마라

-실패 패턴을 살펴라

 

 

' > 클린코드' 카테고리의 다른 글

클린코드 14장  (0) 2021.12.28
클린코드 13장  (0) 2021.12.27
클린코드 10~12장  (0) 2021.12.24
클린코드 7~9장  (0) 2021.12.23
클린코드 4장 - 주석  (0) 2021.07.22
댓글
최근에 올라온 글
최근에 달린 댓글
링크
Total
Today
Yesterday