2025.07.07 ~ 07.08
함수는 한가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한가지만을 해야한다.
- 추상화 수준: "얼마나 구체적/세부적인지 vs 얼마나 개념적인지"를 나타내는 말
- 높은 추상화: 무엇(what)을 한다에 집중(세부 방법은 안 드러냄)
- 낮은 추상화: 어떻게(how)를 한다를 상세히 기술(구현 세부까지 보임)
지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다.
fun renderPageWithSetupsAndTeardowns(page: Page) {
if (page.isTestPage()) {
page.includeSetupAndTeardown()
}
page.renderAsHtml()
}
이 함수는 사실 3단계 작업을 수행하지만, isTestPage, includeSetupAndTeardown, renderAsHtml 모두 "페이지에 무엇을 한다" 라는 동일한 추상화 수준의 작업 단위이다. 즉, "테스트 페이지를 렌더링하기 위해 필요한 동작 수행"이라는 하나의 역할을 수행한다.
fun renderPageWithSetupsAndTeardowns(page: Page) {
if (page.isTestPage()) {
page.includeSetupAndTeardown()
}
// 갑자기 아주 낮은 레벨로 내려감
openFile()
writeBytes("<html>")
closeFile()
}
한 함수 내에 추상화 수준을 섞으면 코드를 읽는 사람이 헷갈린다. 이 함수가 정말 무엇을 책임지는지 혼란을 준다.
근본 개념과 세부사항을 뒤섞기 시작하면, 깨어진 창문처럼 사람들이 함수에 세부사항을 점점 더 추가한다.
위에서 아래로 코드 읽기: 내려가기 규칙
코드는 위에서 아래로 이야기처럼 읽혀야 좋다. 한 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 온다.
코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드라 불러도 되겠다.
이름이 길어도 괜찮다. 겁먹을 필요없다. 길고 서술적인 이름이 짧고 어려운 이름보다 좋다.
이상적인 인수 개수: 0개 > 1개 > 2개 순. 3개는 피하고, 4개 이상은 금지.
단항 함수
isValid(password) - 인수에 질문을 던짐parseInt(text) - 인수를 변환해서 반환이항 함수
Point(x, y)삼항 함수
출력 인수
// Bad: 출력 인수 - 의미 모호
appendFooter(report) // report가 입력? 출력?
fun appendFooter(report: StringBuffer) // 이름만으론 출력인수인지 모름
// Good: 객체 메서드 - 직관적
report.appendFooter() // report가 자신을 수정
함수는 명령(수행) 또는 조회(답변) 중 하나만 해야 함. 둘 다 하면 혼란 야기.
fun set(attribute: String, value: String): Boolean // 명령 + 조회
if (set("username", "unclebob")) { // 혼란스러움
// set이 "설정하다"인지 "설정된"인지 모호
// 동사인지 형용사인지 불분명
}
// 조회와 명령 완전 분리
if (attributeExists("username")) { // 조회: 존재 확인
setAttribute("username", "unclebob") // 명령: 값 설정
}
점진적으로 단계별 접근!
정리하고 싶은 내용이 너무 많은데 글이 길어질까봐 짧게 옮겼다. 이 조차도 글이 길어질까봐 겁 먹고있다. 개발을 하다보면 함수를 계속해서 만들어야 하는데, 추상화 수준이 일정해야하고, 함수명에 인수 순서가 반영 되어야하고, 인수의 개수 또한 너무 많으면 안된다는것을 모르고 있었다. 나는 왜 함수를 한번에 완벽하게 짜려고 했는지 모르겠다. 블로그 글도 계속 읽어보며 다듬어가는데, 코드 또한 단계별로 접근해서 개선해 나가야겠다. 프로젝트 상황에 맞추다 보면 오늘 배운 것들을 제대로 지키지 못할거 같다. 그치만 가이드라인이 있으면 마음이 달라진다. 못지키면 찜찜해진다ㅎㅎ