함수를 잘 만드는 법
👉 2~4줄의 함수, 간결하고 명백하게 읽힌다.
👉 if 문/else 문/while 문 등에 들어가는 블록은 한 줄이어야 한다.
👉 함수는 한 가지만을, 잘 해야한다.
public static String renderPageWithSetupsAndTeardowns(
PageData pageData, boolean isSuite)
if(isTestPage(pageData)){
includeSetupAndTearddownPages(pageData, isSuite);
return pageData.getHtml();
}
👉 한 가지란? 하나의 추상화 수준
위 코드가 하는 일은
1. 페이지가 테스트 페이지인지 판단한다.
2. 그렇다면 설정 페이지와 해제 페이지를 넣는다.
3. 페이지를 HTML로 랜더링한다.
👉 하나의 추상화 수준이란?
👉 함수 내 섹션
함수 내 섹션이 존재한다는 것은 한 함수에서 여러 작업을 한다는 것!
👉 함수가 확실히 한 가지의 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 한다.
👉 추상화 수준이 동일하다는 것?
추상화 되어있는 정도가 동일하다는 것 → 직접적인 정도 or 추상적인 정도
👉 위에서 아래로 내려가듯 읽히는 코드가 좋은 코드
👉 함수의 추상화 수준이 아래로 갈 수록 한 번에 한 단계씩 낮아지는 코드
👉 아래로 갈 수록 상위에서 사용한 함수를 설명하는 느낌으로 (상위에서 쓴 함수를 하위에 붙여서 작성하는 식)
👉 Switch 문을 작게 만들기는 어렵다.
👉 저차원 클래스에 숨기고 절대로 반복하지 않는 방법은 있다. (다형성 이용)
👉 Switch 문 함수, 다음의 원칙 준수하기
👉 겁먹지 말 것. 짧고 어려운 이름보다 길고 서술적인 이름이 좋다.
👉 여러 단어가 쉽게 읽히는 명명법을 사용해 함수의 기능을 잘 표현하는 이름의 조합을 선택한다.
👉 IDE를 활용하면 이름 바꾸기가 더 쉬워진다.
👉 명명법 종류
UtilityBox
utilityBox
utility_box
g_bTrue
IDISPLAY_ClearScreen
DEFAULT_COUNTRY_CODE
👉 함수에서 이상적인 인수 개수는 0개(무항)이다.
👉 삼항은 가능한 피하는 것이 좋다.
인수가 3개 이상으로 넘어가면 유효한 값으로 모든 조합을 구성해 테스트하기가 상당히 부담스러워진다.
👉 4개 이상(다항)은 특별한 이유가 필요하다. 있어도 비추
👉 출력 인수는 비추한다. 익숙하지 않고 이해하기 어려워 코드를 재차 확인하게 만든다.
👉 함수에 인수 1개를 넘기는 경우 2가지 함수에 이름을 지을 때는 두 경우를 분명히 구분한다.
👉 플래그 인수, 함수로 부울 값을 넘기는 관례는 추하다.
👉 함수가 여러가지 일을 한다는 뜻이니까.
👉 인수 2개는 한 값을 표현하는 두 요소여야 한다.
👉 인수 2개의 요소가 자연적인 순서가 없어 인위적으로 기억해야하는 함수는 비추다.
👉 무조건 나쁘다는 뜻이 아니라, 그만큼 위험하다는 것이다.
👉 주춤하게 되는, 무시해야 하는 문제가 두 배 이상 늘어난다.
👉 assertEquals(1.0,amount,.001)
와 같은 경우, 부동소수점 비교가 상대적이기 때문에 가치가 충분하다고 느낀다고 한다.
👉 객체를 생성해 인수를 줄이는 방법은 변수를 묶어 넘기려면 이름을 붙여야하므로 개념을 표현하게 된다. 눈속임조차 될 수 없음.
👉 가변 인수를 취하는 함수는 단항, 이항, 삼항 함수로 취급할 수 있다. 하지만 이를 넘어서는 함수를 사용할 경우는 문제!
👉 단항 함수는 함수와 인수가 동사/명사 쌍을 이뤄야 한다. EX) writeField(name)
👉 부수 효과는 거짓말! 남몰래 한 가지 일 이상을 하니까.
👉 많은 경우 시간적인 결합이나 순서 종속성을 초래한다.
👉 시간적인 결합이 필요하다면 함수 이름에 분명히 명시한다.
👉 출력 인수 대신 this를 사용하라.
👉 EX) appendFooter(s)
→ report.appendFooter()
👉 함수에서 상태를 변경해야 한다면 함수가 속한 객체 상태를 변경하는 방식을 택하라.
👉 함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다.
👉 객체 상태를 변경하거나 객체 정보를 반환하거나 둘 중 하나다.
👉 명령 함수에서 오류 코드를 반환하는 방식은 명령/조회 분리 규칙을 위반한다.
👉 오류 처리 역시 한 가지 작업이다.
👉 코드 구조에 혼란을 일으키고, 정상 동작과 오류 처리 동작을 뒤섞는다.
👉 try/catch 블록을 별도 함수로 뽑아내는 편이 좋다.
public void delete(Page page){
try{
deletePageAndAllReferences(page);
}catch(Exception e){
logError(e);
}
}
private void deletePageAndAllReferences(page page) throws Exception{
//deletePage...
}
private void logError(Exception e){
//log...
}
👉 오류 코드를 반환한다는 것은 어디선가 오류 코드를 정의한다는 뜻이다.
👉 오류 코드 대신 예외를 사용하면 새 예외는 Exception 클래스에서 파생된다. 따라서 재컴파일/재배치 없이도 새 예외 클래스를 추가할 수 있다.
👉 중복 제거 전략
👉 ❌
👉 구조적 프로그래밍의 목표와 규율은 함수가 작다면 별 이익을 얻지 못함.
👉 함수가 작다면 return, break, continue를 여러 차례 사용해도 괜찮음.
👉 처음부터 짜내긴 어렵다.
👉 처음을 다듬고, 함수를 만들고, 이름을 바꾸고, 중복을 제거하고, 메서드를 줄이고, 순서를 바꾼다. 때로는 전체 클래스를 쪼개기도 한다.
👉 와중에 코드는 항상 단위 테스트를 통과한다.
👉 작성하는 함수가 분명하고 정확한 언어로 깔끔하게 같이 맞아떨어져야 이야기를 풀어가기 쉬워진다.
어떤 코드든 절대로 무시하면 안된다. 무시한 코드에 오류가 숨어드니까.
많은 원칙과 기법이 중복을 없애거나 제어할 목적으로 나왔다.
대가 프로그래머는 시스템을 '구현할' 프로그램이 아니라 '풀어갈' 이야기로 여긴다.
프로그래밍 언어라는 수단을 사용해 좀 더 풍부하고 좀 더 표현력이 강한 언어를 만들어 이야기를 풀어나간다.