티스토리 뷰
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