Skip to content

Latest commit

 

History

History
145 lines (120 loc) · 7.19 KB

ch03_함수.md

File metadata and controls

145 lines (120 loc) · 7.19 KB

3. 함수

작게 만들어라

  • 작은 함수가 좋다

블록과 들여쓰기

  • if 문, else 문, while 문에 들어가는 블록은 1줄이어야 한다
  • 즉 중첩 구조가 생길만큼 함수가 커저서는 안된다.

한 가지만 해라!

  • 함수는 한 가지를 해야 한다.
  • 지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다.
  • 즉 1개의 함수는 1개의 추상화 수준을 처리한다.
  • 의미 있는 이름으로 다른 함수를 추출할 수 있더면 그 함수는 여러 작업을 하는 것이다

함수 내 섹션

  • 한 가지 작업만 하는 함수는 자연스럽게 섹션으로 나누기 어렵다.(if, else if, else)

함수 당 추상화 수준은 하나로!

  • 함수가 확실히 '한 가지' 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 한다.
getHtml() // 추상화 수준이 아주 높다
String pagePathName = PathParser.render(pagepath); // 추상화 수준이 보통이다
example.append("\n"); // 추상화 수준이 아주 낮다.
  • 한 함수 내에 추상화 수준을 섞으면 코드를 읽기 어렵다.
  • 근본 개념과 세부사항을 꼭 분리하여라
  • 근본 개념 함수를 만들고 그 안에 세부사항 함수를 만들어 추상화 수준으로 고수준으로 저수준으로 자연스럽게 이어지게 만든다.

위에서 아래로 코드 읽기: 내려가기 규칙

  • 코드는 위에서 아래로 이야기처럼 읽혀야 한다.
  • 한 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 온다.
  • 위에서 아래로 프로그램을 읽으면 함수 추상화 수준이 한 번에 한 단계씩 낮아진다.
  • 내려가기 규칙
  • 핵심은 짧으면서도 '한 가지' 만 하는 함수를 만들자

Switch 문

  • Switch 문은 짧게 만들기 어렵다
  • 너무 길고, 단일블록 함수가 아니며 한가지 작업만 하게 만들기 어렵다.
  • 본질적으로 switch 문은 N 가지를 처리한다.
  • 저자는 다형성 switch 문을 저차원 클래스에 숨기고 절대로 반복하지 않는 방법을 사용한다.
  • switch 문을 추상 팩토리에 꽁꽁 숨긴후 다형성으로 파생 클래스의 함수를 실행한다.
  • 상속 관계로 숨긴 후에는 절대 다른 코드에 노출하지 않는다.
  • ch03_ex1 참고

서술적인 이름을 사용하라

  • 이름이 길어도 괜찮다
  • 길고 서술적인 이름이 짧고 어려운 이름보다 좋다
  • 이름을 붙일때는 일관성이 있어야 한다.(모듈 내에서 함수 이름은 같은 문구, 명사, 동사를 사용한다)

함수 인수

  • 이상적인 인수 개수는 0개 이다
  • 다음은 1개, 2개다.
  • 3개는 가능한 피하고 4개 이상은 특별한 이유가 필요하나 이유가 있어도 사용하면 안된다.
  • 인수 사용시 함수이름과 인수 사이에 추상화 수준이 다르다면 코드 이해가 어렵다
  • 추가로 불필요한 인수를 사용하면 현 시점에서 별로 중요하지 않은 세부사항을 알아야 한다.
  • 출력 인수는 이해하기 더 어렵다.

많이 쓰는 단항 형식

  • 인수에 질문을 던지는 경우이다.
  • 인수를 뭔가로 변환해 결과를 반환하는 경우이다
  • 이벤트이다(입력 인수만 존재)
  • 이외에는 단항 함수를 피하자

플래그 인수

  • 함수로 부울 값을 넘기면 함수가 한꺼번에 여러가지를 처리한다는 공표이다. 피하자

이항 함수

  • 이해가 어렵다. 가끔 무시해도 될거같은 인수가 보인다(OutputStream)
  • 하지만 어떤 코드든 절대로 무시하면 안되고 무시한 코드에 오류가 숨어든다.
  • 2개 인수간에 자연적인 순서가 없다면 헷갈리는 코드가 된다.
  • 가능하면 단항 함수로 바꿔라 writeField(outputStream, name) -> writeField(name)
    1. writeField 메서드를 outputStream 클래스 구성원으로 만들어 outputStream.writeField(name)호출
    1. outputStream 을 현재 클래스 구성원 변수로 만들어 인수로 넘기지 않는다
    1. FieldWriter 라는 새 클래서 생성하여 구성자로 outputStream 을 받고 write 메서드를 구현

삼항 함수

  • Nope

인수 객체

  • 클래스로 묶어 개념을 표현할 수 있다
  • double x, double y -> Point center

인수 목록

  • 가변 함수는 가변 인수 전부를 동등하게 취급하면 List 형 인수 하나로 취급한 후 이항 함수로 사용가능

동사와 키워드

  • 단항 함수는 함수와 인수가 동사/명사 쌍을 이뤄야 한다.
    • writeField(name)
  • 함수 이름에 키워드를 추가하자
    • assertExpectedEqualsActual(expected, actual)

부수효과를 일으키지 마라

  • 시간적인 결합과 순서 종속성을 초래한다.
    • Session.initializer()라는 함수로 인해 세션 초기화가 일어날 수 있다.
    • 함수 이름을 checkPasswordAndInitializeSession 으로 바꾸자 물론 함수가 2가지 역할을 하지만

출력 인수

  • 객체 지향 언어에서는 출력인수를 사용할 필요가 없다
  • 출력인수로 사용하라고 설계한 변수가 바로 this 이다
  • report.appendFooted()
  • 일반적으로 출력인수는 피해야 한다
  • 함수에서 상태를 변경해야 한다면 함수가 속한 객체 상태를 변경하는 방식을 택하자

명령과 조회를 분리하라!

  • 함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다.

오류 코드보다 예외를 사용하라

  • 오류 코드 사용시 if 문에서 명령을 표현식으로 사용하기 쉽다
  • 오류 코드 반환시 호출자는 오류 코드를 곧바로 처리하므로 if 아도겐 코드가 만들어질 수 있다.
  • 예외 사용시 오류 처리 코드가 원래 코드에서 분리되어 코드가 깔끔하다

Try/Catch 블록 뽑아내기

  • try/catch 블록은 원래 추하다
  • 해당 블록을 별도 함수로 뽑아내자
  • 이러면 정상 동작과 오류처리 동작을 분리하여 코드를 이해하고 수정하기 쉬워진다.
try{
    // 실행 함수 호출
}catch(Exception e){
    // 오류 처리 동작 함수 호출
}

오류 처리도 한가지 작업이다

  • 오류를 처라하는 함수는 오류만 처리하게 하자

Error.java 의존성 자석

  • 오류 코드 반환 시 오류 코드 수정시 해당 코드를 사용하는 클래스를 다시 컴파일하고 다시 배치해야한다(Enum)
  • 오류 코드 대신 예외를 사용하면 새 예외는 Exception 클래스에서 파생된다.
  • 재컴파일/재배치 없이도 새 예외 클래스 추가 가능

반복하지 마라!

  • 중복이 문제다
  • 중복은 모든 악의 근원이다

구조적 프로그래밍

  • 모든 함수와 함수 내 모든 블록에 입구와 출구가 하나만 존재해야 한다.
  • 루프 안에서 break, continue, goto 를 사용하지 말고 return 1개만 있어야 한다.
  • 하지만 함수를 작게 만든다면 return break continue 를 여러차례 사용해도 괜찮다.

함수를 어떻게 짜죠?

  • 일단 싸질르고 리팩토링해라

결론

  • 이 모든 내용의 목적은 시스템이라는 이야기를 풀어가는 데 있다.