- if 문, else 문, while 문에 들어가는 블록은 1줄이어야 한다
- 즉 중첩 구조가 생길만큼 함수가 커저서는 안된다.
- 함수는 한 가지를 해야 한다.
- 지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다.
- 즉 1개의 함수는 1개의 추상화 수준을 처리한다.
- 의미 있는 이름으로 다른 함수를 추출할 수 있더면 그 함수는 여러 작업을 하는 것이다
- 한 가지 작업만 하는 함수는 자연스럽게 섹션으로 나누기 어렵다.(if, else if, else)
- 함수가 확실히 '한 가지' 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 한다.
getHtml() // 추상화 수준이 아주 높다
String pagePathName = PathParser.render(pagepath); // 추상화 수준이 보통이다
example.append("\n"); // 추상화 수준이 아주 낮다.
- 한 함수 내에 추상화 수준을 섞으면 코드를 읽기 어렵다.
- 근본 개념과 세부사항을 꼭 분리하여라
- 근본 개념 함수를 만들고 그 안에 세부사항 함수를 만들어 추상화 수준으로 고수준으로 저수준으로 자연스럽게 이어지게 만든다.
- 코드는 위에서 아래로 이야기처럼 읽혀야 한다.
- 한 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 온다.
- 위에서 아래로 프로그램을 읽으면 함수 추상화 수준이 한 번에 한 단계씩 낮아진다.
- 내려가기 규칙
- 핵심은 짧으면서도 '한 가지' 만 하는 함수를 만들자
- Switch 문은 짧게 만들기 어렵다
- 너무 길고, 단일블록 함수가 아니며 한가지 작업만 하게 만들기 어렵다.
- 본질적으로 switch 문은 N 가지를 처리한다.
- 저자는 다형성 switch 문을 저차원 클래스에 숨기고 절대로 반복하지 않는 방법을 사용한다.
- switch 문을 추상 팩토리에 꽁꽁 숨긴후 다형성으로 파생 클래스의 함수를 실행한다.
- 상속 관계로 숨긴 후에는 절대 다른 코드에 노출하지 않는다.
- ch03_ex1 참고
- 이름이 길어도 괜찮다
- 길고 서술적인 이름이 짧고 어려운 이름보다 좋다
- 이름을 붙일때는 일관성이 있어야 한다.(모듈 내에서 함수 이름은 같은 문구, 명사, 동사를 사용한다)
- 이상적인 인수 개수는 0개 이다
- 다음은 1개, 2개다.
- 3개는 가능한 피하고 4개 이상은 특별한 이유가 필요하나 이유가 있어도 사용하면 안된다.
- 인수 사용시 함수이름과 인수 사이에 추상화 수준이 다르다면 코드 이해가 어렵다
- 추가로 불필요한 인수를 사용하면 현 시점에서 별로 중요하지 않은 세부사항을 알아야 한다.
- 출력 인수는 이해하기 더 어렵다.
- 인수에 질문을 던지는 경우이다.
- 인수를 뭔가로 변환해 결과를 반환하는 경우이다
- 이벤트이다(입력 인수만 존재)
- 이외에는 단항 함수를 피하자
- 함수로 부울 값을 넘기면 함수가 한꺼번에 여러가지를 처리한다는 공표이다. 피하자
- 이해가 어렵다. 가끔 무시해도 될거같은 인수가 보인다(OutputStream)
- 하지만 어떤 코드든 절대로 무시하면 안되고 무시한 코드에 오류가 숨어든다.
- 2개 인수간에 자연적인 순서가 없다면 헷갈리는 코드가 된다.
- 가능하면 단항 함수로 바꿔라
writeField(outputStream, name) -> writeField(name)
-
- writeField 메서드를 outputStream 클래스 구성원으로 만들어
outputStream.writeField(name)
호출
-
- outputStream 을 현재 클래스 구성원 변수로 만들어 인수로 넘기지 않는다
-
- FieldWriter 라는 새 클래서 생성하여 구성자로 outputStream 을 받고 write 메서드를 구현
- 클래스로 묶어 개념을 표현할 수 있다
double x, double y -> Point center
- 가변 함수는 가변 인수 전부를 동등하게 취급하면 List 형 인수 하나로 취급한 후 이항 함수로 사용가능
- 단항 함수는 함수와 인수가 동사/명사 쌍을 이뤄야 한다.
- 함수 이름에 키워드를 추가하자
assertExpectedEqualsActual(expected, actual)
- 시간적인 결합과 순서 종속성을 초래한다.
Session.initializer()
라는 함수로 인해 세션 초기화가 일어날 수 있다.
- 함수 이름을
checkPasswordAndInitializeSession
으로 바꾸자 물론 함수가 2가지 역할을 하지만
- 객체 지향 언어에서는 출력인수를 사용할 필요가 없다
- 출력인수로 사용하라고 설계한 변수가 바로 this 이다
report.appendFooted()
- 일반적으로 출력인수는 피해야 한다
- 함수에서 상태를 변경해야 한다면 함수가 속한 객체 상태를 변경하는 방식을 택하자
- 함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다.
- 오류 코드 사용시 if 문에서 명령을 표현식으로 사용하기 쉽다
- 오류 코드 반환시 호출자는 오류 코드를 곧바로 처리하므로 if 아도겐 코드가 만들어질 수 있다.
- 예외 사용시 오류 처리 코드가 원래 코드에서 분리되어 코드가 깔끔하다
- try/catch 블록은 원래 추하다
- 해당 블록을 별도 함수로 뽑아내자
- 이러면 정상 동작과 오류처리 동작을 분리하여 코드를 이해하고 수정하기 쉬워진다.
try{
// 실행 함수 호출
}catch(Exception e){
// 오류 처리 동작 함수 호출
}
- 오류 코드 반환 시 오류 코드 수정시 해당 코드를 사용하는 클래스를 다시 컴파일하고 다시 배치해야한다(Enum)
- 오류 코드 대신 예외를 사용하면 새 예외는 Exception 클래스에서 파생된다.
- 재컴파일/재배치 없이도 새 예외 클래스 추가 가능
- 모든 함수와 함수 내 모든 블록에 입구와 출구가 하나만 존재해야 한다.
- 루프 안에서 break, continue, goto 를 사용하지 말고 return 1개만 있어야 한다.
- 하지만 함수를 작게 만든다면 return break continue 를 여러차례 사용해도 괜찮다.
- 이 모든 내용의 목적은 시스템이라는 이야기를 풀어가는 데 있다.