2023년 4월 20일 작성
Flag 인수(
Clean Code - 좋은 함수 만들기
함수는 한 가지만 해야 합니다.
좋은 함수를 만드는 방법
- 좋은 함수는 한 가지만 하는, 작고, 잘 이름 붙여진 함수입니다.
작게 만들기
- 하나의 함수는 하나의 이야기만 표현해야 합니다.
들여쓰기 수준을 낮게 유지하기
- 함수의 들여쓰기 수준은 1단이나 2단을 넘어서면 안 됩니다.
- 중첩 구조가 생길만큼 함수가 커져서는 안 됩니다.
if-else
,while
문 등에 들어가는 block은 한 줄이어야 합니다.- block에 한 줄만 들어가면, 바깥을 감싸는 함수(enclosing function)가 작아집니다.
- block 안에서 호출하는 함수 이름을 적절히 짓는다면, code를 이해하기 쉬워집니다.
한 가지만 하기
함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야 한다.
- 함수가 한 가지만 하는지 판단할 수 있습니다.
- 한 함수 안(같은 추상화 수준)에서 logic을 추상화하여 한 단계만 수행하는 지 확인합니다.
- 다른 표현이 아니라 의미 있는 이름으로 다른 함수를 추출할 수 있는지 확인합니다.
- 한 가지 작업만 하는 함수는 section으로 나누기 어렵기 때문입니다.
함수의 추상화 수준을 맞추기
- code는 위에서 아래로 이야기처럼 읽히는 것이 좋습니다.
- 따라서 함수 당 추상화 수준은 하나여야 합니다.
- 함수가 확실히 한 가지 작업만 하려면, 함수 내 모든 문장의 추상화 수준이 동일해야 합니다.
- 한 함수 내에 여러 추상화 수준을 섞으면 code를 읽는 사람이 헷갈리게 됩니다.
- 특정 표현이 근본 개념인지 세부사항인지 구분하기 어렵게 되기 때문입니다.
switch
문 숨기기
switch
문은 작게 만들기 어렵습니다.switch
문은 본질적으로 여러 가지를 처리하도록 만들어졌기 때문입니다.- 분기가 많은
if-else
문도switch
문과 같은 특징을 가집니다.
switch
문은 단일 책임 원칙(SRP, Single Responsibility Principle)을 위반합니다.- code를 변경할 이유가 여러 개이기 때문입니다.
switch
문은 개방-폐쇄 원칙(OCP, Open Closed Principle)을 위반하게 합니다.- 새로운 case를 추가할 때마다 code를 변경해야 하기 때문입니다.
switch
문을 완전히 피할 방법은 없기 때문에, 각switch
문을 저차원 class에 숨기고 반복하지 않는 방식을 사용합니다.- 숨긴 후에는 다른 code에 노출시키지 않습니다.
서술적인 이름 사용하기
code를 읽으면서 짐작했던 기능을 각 routine이 그대로 수행한다면, clean code다.
- 함수가 작고 단순할수록 서술적인 이름을 고르기 쉬워집니다.
- 이름이 길어도 괜찮습니다.
- ‘길고 서술적인 이름’이 ‘짧고 어려운 이름’보다 좋습니다.
- ‘길고 서술적인 이름’이 ‘길고 서술적인 주석’보다 좋습니다.
- 서술적인 이름을 사용하면 설계가 뚜렷해지므로 code 개선이 쉬워집니다.
- 일관성 있는 이름을 붙여야 합니다.
- module 내에서 함수 이름은 같은 문구, 명사, 동사를 사용합니다.
- 예를 들어,
includeSetupAndTeardownPages
,includeSetupPages
,includeSuiteSetupPage
,includeSetupPage
, …
- 예를 들어,
- module 내에서 함수 이름은 같은 문구, 명사, 동사를 사용합니다.
인수를 적게 유지하기
함수 | 인수 |
---|---|
무항 | 0개 |
단항 | 1개 |
이항 | 2개 |
삼항 | 3개 |
다항 | 4개 이상 |
- 함수의 인수 갯수는 적을수록 좋습니다.
- 인수가 많으면 개념을 이해하기 어렵습니다.
-
인수가 많으면 test case를 작성하기 어렵습니다.
- 인수가 2개 이상 필요하다면, 인수 객체를 생성해 인수를 줄일 수도 있습니다.
- 인수의 일부를 독자적인 class 변수로 선언하여 사용합니다.
- class 내에서는 변수를 묶기 위해 이름을 붙여 개념을 표현합니다.
- 인수의 일부를 독자적인 class 변수로 선언하여 사용합니다.
함수 이름에 인수를 넣기
- 함수(동사)와 인수(명사)가 쌍을 이루면 한번에 알아보기 쉬운 함수 이름이 됩니다.
write(name)
보다writeField(name)
이 더 낫습니다.- 단항 함수에 적용할 수 있습니다.
- 함수 이름에 keyword(인수 이름)를 추가하면 인수 순서를 기억할 필요가 없습니다.
assertEquals(expected, actual)
보다assertExpectedEqualsActual(expected, actual)
이 더 낫습니다.
출력 인수 사용하지 않기
입력 인수 | 출력 인수 |
---|---|
함수에 입력되는 인수 | 함수를 거치고 나오는 입력되었던 인수 |
- 입력 인수를 변환해야 한다면, 변환 결과를 반환 값으로 돌려줍니다.
- 변환 결과를 가져오기 위해 출력 인수를 사용하는 것보다, 반환 값을 사용하는 것이 낫습니다.
- 객체 지향 언어에서는 출력 인수를 사용할 필요가 거의 없습니다.
- 출력 인수로 사용하기 위해 설계한
this
라는 변수가 있기 때문입니다. - 예를 들어,
appendFooter(s);
는s
를 바닥글로 첨부 할지,s
에 바닥글을 첨부 할지 알 수 없습니다.appendFooter(s)
보다report.appendFooter()
가 더 낫습니다.
- 출력 인수로 사용하기 위해 설계한
- 함수에서 상태를 변경해야 한다면, 함수가 속한 객체 상태를 변경하는 방식을 사용하는 것이 더 좋습니다.
인수의 추상화 수준 맞추기
- 함수 이름과 인수는 추상화 수준이 같아야 합니다.
Flag 인수(boolean
)를 사용하지 않기
- flag 인수를 받는다는 것은, 함수가 한꺼번에 여러 가지를 처리한다는 뜻입니다.
- 함수는 한 번에 한 가지만 처리해야 합니다.
부수 효과 일으키지 않기
- 부수 효과는 그 자체로 거짓말이 됩니다.
- 함수에서 한 가지를 하는 척 하면서 다른 것도 하기 때문입니다.
명령과 조회를 분리하기
- 함수는 명령과 조회 중 하나만 해야 합니다.
Error Code보다 예외를 사용하기
- 오류 처리도 ‘한 가지 작업’이기 때문에, 오류를 처리하는 함수는 오류만 처리해야 합니다.
try-catch-finaly
문을 사용합니다.
반복하지 않기
- 중복이 늘어나면, code 길이가 늘어나고, 수정 시 변경점이 많아집니다.
- 중복을 없애면 module 가독성이 좋아집니다.
- 많은 원칙, 기법, 기술이 중복을 제거하고 제어할 목적으로 나왔습니다.
다듬기
- software를 짜는 행위는 글짓기와 비슷합니다.
- 생각을 기록한 후 읽기 좋게 다듬습니다.
- 초안은 서투르고 어수선하므로, 원하는 대로 읽힐 때까지 읽기 좋게 다듬습니다.
Reference
- Clean Code (도서) - Robert C. Martin