본문 바로가기
Book Record

[Clean Code] Chapter3. 함수

by 그냥팬더 2021. 4. 20.
반응형

Clean Code

by. Robert C. Martin

03. 함수

어떻게 함수를 읽기 쉽고 이해하기 쉽게 만들 수 있을까?
의도를 분명히 표현하는 함수를 어떻게 구현할 수 있을까?
함수에 어떤 속성을 부여해야 처음 읽는 사람이 프로그램 내부를 직관적으로 파악할까?

작게 만들어라!

함수를 만드는 규칙

  1. 작게!
  2. 더 작게!

각 함수는 명백하게 하나의 이야기를 표현해야 한다.

블록과 들여쓰기

if / else / while 문 등에 들어가는 블록은 한 줄이어야 한다.
대체로 블록 안에서 함수를 호출한다.
이때 함수 이름을 적절하게 짓는다면, 코드를 이해하기도 쉽다.

중첩 구조가 생길만큼 함수가 커져서는 안된다.
즉, 함수에서의 들여쓰기 수준은 1단이나 2단을 넘어서면 안된다.

한 가지만 해라!

  • 함수는 한 가지를 해야한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야 한다.
  • 지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행해야 한다.
    • 함수는 큰 개념을 다음 추상화 수준에서 여러 단계로 나눠 수행하기 위해 만든다.
  • 만약, 의미 있는 이름으로 다른 함수를 추출한다면, 함수는 여러 작업을 하는셈이다.

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

함수 내 모든 문장의 추상화 수준이 동일해야 한다.

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

  • 코드는 위에서 아래로 이야기처럼 읽혀야 좋다.
    • 위에서 아래로 프로그램을 읽으면 함수 추상화 수준이 한 번에 한 단계씩 낮아진다.
  • 물론 추상화 수준이 하나인 함수 구현은 어렵다.
    • 그렇지만 매우 중요한 규칙이다.

Switch 문

Switch 문은 작게 만들기 어렵다.
본질적으로 '한 가지'만 하는 switch문도 어렵다. switch문은 N가지를 수행한다.

각 switch문을 저차원 클래스에 숨기고, 다형성을 이용해 절대 반복하지 않는 방법이 있다.

public Money calculatePay(Employee e) throws InvalidEmployeeType {
    switch (e.type) {
        case COMMISSIONED:
            return calculateCommissionedPay(e);
        case HOURLY:
            return calculateHourlyPay(e);
        case SALARIED:
            return calculateSalariedPay(e);
        default:
            throw new InvalidEmployeeType(e.type);
    }
}

위 함수는 몇 가지 문제가 있다.

  1. 함수가 길다.
  2. '한 가지' 작업만 수행하지 않는다.
  3. SRP를 위반한다.
    • 한 클래스는 하나의 책임만을 가져야 한다.
  4. OCP를 위반한다.
    • 확장은 가능하지만, 변경은 불가능 해야한다.
public abstract class Employee {
    public abstract Money calculatePay();
}

public interface EmployeeFactory {
    public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
}

public class EmployeeFactoryImpl implements EmployeeFactory {
    public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType {
        switch (r.type) {
            case COMMISSIONED:
                return new CommissionedEmployee(r);
            case HOURLY:
                return new HourlyEmployee(r);
            case SALARIED:
                return new SalariedEmployee(r);
            default:
                throw new InvalidEmployeeType(r.type);
        }
    }
}

switch 문을 추상 팩토리에 숨긴다.

팩토리는 switch 문을 사용해 Employee의 파생 클래스의 인스턴스를 생성하고, 함수들은 인스턴트를 거쳐 실행된다.

서술적인 이름을 사용하라!

  • 함수가 하는 일을 좀 더 잘 표현하는 이름
  • 함수가 작고 단순할수록 서술적인 이름을 짓기 더 쉽다.
  • 길고 서술적인 이름 > 짧고 어려운 이름
  • 이름을 붙일 때는 일관성이 있어야한다.

함수 인수

  • 함수에서 가장 이상적인 인수는 0개(무항).

    • 그 다음은 1개(단항), 다음은 2개(이항)
    • 그 이상은 가능한한 피하는 편이 좋다.
    • 4개 이상(다항)은 특별한 이유가 필요하다. 하지만 사용하면 안된다.
  • 코드를 읽는 사람이 세부사항을 알아야하는 경우가 생기기 때문에

많이 쓰는 단항 형식

  1. 인수에 질문을 던지는 경우
  2. 인수를 변환해 결과를 반환하는 경우
  3. 이벤트
    • 입력만 있고, 출력은 없다.

위 경우가 아니라면 단항 함수는 피한다.

플래그 인수

플래그 인수는 추하고 끔찍하다.

함수가 여러가지를 행하는 것을 의미

인수 객체

  • 인수가 2-3개 필요하다면 일부를 독자적인 클래스 변수로 선언할 가능성이 높다.
    • 하지만 이미 개념을 표현하게 된다

동사와 키워드

좋은 함수 이름은 함수와 인수가 동사와 명사 쌍을 이뤄야 한다.

부수 효과를 일으키지 마라!

  • 부수 효과는 거짓말
    • 함수는 한 가지를 하겠다고 하고, 다른 일을 하는 것
  • 예상치 못하게 클래스 변수를 수정하거나, 전역 변수를 수정한다.
  • 시간적인 결합이나 순서 종속성을 초래한다.

명령과 조회를 분리하라!

  • 함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다.
    • 객체를 변경하거나 아니면 객체 정보를 반환하거나 둘 중 하나

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

  • 명령 함수에서 오류 코드를 반환하는 방식은 명령/조회 분리를 위반한다.
    • 오류를 반환하게 되면, 호출자는 오류 코드를 처리해야 한다.
  • 예외를 사용하면 오류 처리 코드가 원래 코드에서 분리되므로 코드가 깔끔.

try-catch 블록 뽑아내기

  • 코드 구조에 혼란을 일으키며, 정상 동작과 오류 처리 동작을 섞는다.
    • 별도의 함수로 뽑아내자
  • 정상 동작과 오류 처리 동작을 분리하면 코드를 이해하고 수정하기 쉬어진다.

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

  • 오류를 처리하는 함수는 오류만 처리해야 마땅하다.
  • 함수에 try 키워드가 있다면, 함수는 try로 시작해 catch/finally로 끝나야 한다.

반복하지 마라!

  • 중복은 코드 길이가 늘어날 뿐 아니라, 알고리즘이 변하면 중복된 곳 모두를 손봐야 한다.
  • 중복을 줄이면 모듈의 가독성이 늘어난다.
  • 객체지향에서는 코드를 부모 클래스로 몰아 중복을 없앤다.
  • 구조적 프로그래밍, AOP, COP 모두 중복제거 전략이다.

구조적 프로그래밍

  • 모든 함수와 함수 내 블록에 입구와 출구는 하나만 존재해야 한다.
    • 함수는 return이 하나여야 한다.
    • break나 continue를 사용해선 안되며, goto는 절대로 안된다.
  • 함수가 작게 만든다면 간혹 return, break, continue를 사용해도 괜찮다.

함수를 어떻게 짜죠?

  • 처음엔 길고 복잡하다.
    • 많은 들여쓰기 단계
    • 많은 중복된 루프
    • 긴 인수 목록
    • 즉흥적인 이름
    • 중복된 코드
  • 코드를 다듬는다.
    • 함수를 만든다.
    • 이름을 바꾼다.
    • 중복을 제거한다.
    • 메서드를 줄인다.
    • 순서를 바꾼다.
반응형

댓글