---
description: 클린 코드 3장을 요약한 내용입니다.
---

# 3장 함수

## 작게 만들어라

저자는 이 규칙에 대해 근거를 대기는 어렵지만 40여년 동안의 경험을 바탕으로 작은 함수가 좋다고 판단했다.

```java
public static String renderPageWithSetupsAndTeardowns(PageData pageData, boolean isSuite) throws Exception {
	boolean isTestPage = pageData.hasAttribute("Test");
	if (isTestPage) {
		WikiPage testPage = pageData.getWikiPage();
		StringBuffer newPageContent = new StringBuffer();
		includeSetupPages(testPage, newPageContent, isSuite);
		newPageContent.append(pageData.getContent());
		includeTeardownPages(testPage, newPageContent, isSuite);
		pageData.setContent(newPageContent.toString());
	}
	return pageData.getHtml();
}
```

위 코드도 길다. 되도록 한 함수당 3\~5줄 이내로 줄이는 것을 권장한다.

```java
public static String renderPageWithSetupsAndTeardowns(PageData pageData, boolean isSuite) throws Exception {
        if (isTestPage(pageData))
                includeSetupAndTeardownPages(pageData, isSuite);
        return pageData.getHtml();
}
```

**블록과 들여쓰기**

if/else/while문 등에 들어가는 블록은 함수를 호출하는 한 줄이어야 한다. 들여쓰기 수준이 2단을 넘어서지 않고, 함수 이름을 적절히 짓는다면 더욱 이해하기 쉬워진다.

## 한 가지만 해라

> 함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야 한다.

지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만을 수행한다면 그 함수는 한 가지 작업만을 한다.

### **함수 당 추상화 수준은 하나로**

함수가 ‘한 가지’ 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 한다. 한 함수 내에 추상화 수준을 섞으면 코드를 읽는 사람이 헷갈린다.

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

코드는 위에서 아래로 이야기처럼 읽혀야 좋다. 함 수 추상화 수준이 한 번에 한 단계씩 낮아지는 것이 좋다.

### Switch 문

```java
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);
	}
}
```

Switch 문은 본질적으로 N가지 일을 처리해서 작게 만들기 어렵지만, 되도록 다형성을 이용해 추상 팩토리(abstract factory)에 숨겨서 사용하면 좋다.

```java
public abstract class Employee {
	public abstract boolean isPayday();
	public abstract Money calculatePay();
	public abstract void deliverPay(Money pay);
}
-----------------
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 SalariedEmploye(r);
			default:
				throw new InvalidEmployeeType(r.type);
		}
	}
}
```

그래서 저차원 클래스에 숨기고 반복하지 않기로 한다.

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

> 코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 클린 코드라 불러도 되겠다.

함수에 좋은 이름을 붙인다면 이런 원칙을 달성하기 위해 절반은 성공했다. 함수가 작고 단순할수록 서술적인 이름을 고르기도 쉬워진다.

**Tip**

* 길고 서술적인 이름이 짧고 어려운 이름보다 좋다.
* 시간을 들여서 이름을 정해도 좋다. 좋은 이름은 설계를 더 뚜렷해지게 한다.
* 모듈 내에서 함수 이름은 같은 문구, 명사, 동사를 사용한다.

**함수 인수**

함수 인수는 적을수록 좋다. 이상적인 인수 개수는 0개이고, 3개 이상은 가능한 피하는 편이 좋다. 출력 인수(함수의 반환값이 아닌 인수로 결과를 받는 경우)는 이해하기 어려우므로 쓰지 않는 것이 좋겠다.

**플래그 인수**

플래그 인수는 추하다. 함수로 bool 값을 넘기는 건 여러가지 일을 처리한다고 공표하는 셈이다. 그래서 저차원 클래스에 숨기고 반복하지 않기로 한다.

**이항 함수**

인수가 2개인 함수는 1개인 함수보다 이해하기 어렵다.

다만 Point 클래스와 같이 일반적으로 2개의 인수를 취하는 적절한 경우에는 이항 함수도 적절하다.

```java
Point p = new Point(x,y);
```

**삼항 함수**

삼항 함수는 훨씬 더 이해하기 어려우므로 신중히 고려해야 한다. 순서, 주춤, 무시로 야기되는 문제가 생긴다.

**인수 객체**

인수가 2-3개 필요하다면 일부를 독자적인 클래스 변수로 선언해보자.

```java
Circle makeCircle(double x, double y, double radius);
Circle makeCircle(Point venter, double radius);
```

**인수 목록**

때로는 String.format과 같이 인수 개수가 가변적인 경우도 필요하다. String.format의 인수는 List 형 인수이기 때문에 이항 함수라고 할 수 있다.

**동사와 키워드**

함수의 의도를 표현하려면 좋은 함수 이름이 필요하다.

단항 삼수는 함수와 인수가 동사/명사 쌍을 이루면 좋다. `write(name)`

함수 이름에 키워드를 추가하면 인수 순서를 기억할 필요가 없어진다. `assertExpectedEqualsActual(expected, actual)`

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

부수 효과는 거짓말이다. 한 가지를 하겠다고 약속하고선 남몰래 다른 짓을 하는 것이므로, 한 함수는 한 가지만 하도록 하자.

아래 함수에서 Session.initialize() 호출은 함수 이름과 맞지 않는 부수효과다.

```java
public class UserValidator {
	private Cryptographer cryptographer;
	public boolean checkPassword(String userName, String password) {
		User user = UserGateway.findByName(userName);
		if (user != User.NULL) {
			String codedPhrase = user.getPhraseEncodedByPassword();
			String phrase = cryptographer.decrypt(codedPhrase, password);
			if ("Valid Password".equals(phrase)) {
				Session.initialize();
				return true;
			}
		}
		return false;
	}
}
```

**출력 인수**

일반적으로 출력 인수는 피해야 한다. 함수에서 상태를 변경해야 한다면 함수가 속한 객체 상태를 변경하는 방식을 택하자.

```java
report.appendFooter();
```

## 명령과 조회를 분리하라

함수는 객체 상태를 변경하거나 객체 정보를 반환하거나 둘 중 하나만 해야 한다.

`if (set(”username”, “unclebob))` 는 객체의 상태를 변경하는지, 확인하는 코드인지 분간하기 어렵다.

`if (attributeExists(”username”)) setAttribute(”username”, “unclebob”);` 명령과 조회를 분리해 혼란을 제거해주는 것이좋다.

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

오류 코드를 반환하는 방식은 오류를 곧바로 처리해야 하는 문제를 발생시킨다. `if (deletePage(page) == E_OK)`

예외를 사용하면 원래 코드와 분리하여 오류를 처리할 수 있다.

```java
try {
    deletePage(page);
    registry.deleteReference(page.name);
    configKeys.deleteKey(page.name.makeKey());
} catch(Exception e) {
    log.error(e.getMessage());
}
```

**Try/Catch 블록 뽑아내기**

정상 동작과 오류 처리 동작이 함께 존재하는건 추하다. try/catch 블록을 별도 함수로 뽑아내는 편이 좋다.

```java
public void delete(Page page) {
    try {
        deletePageAndAllReferences(page);
    } catch (Exception e) {
        logError(e);
    }
}
private void deletePageAndAllReferences(Page page) throws Exception {
    // 정상 동작
}
private void logError(Exception e) {
    // 오류 처리 동작
}
```

## 반복하지 마라

중복 코드는 코드의 길이를 증가시키고, 변경에 불리하다.

예를 들면, 관계형 데이터베이스의 정규형과 객체지향 프로그래밍의 상속은 중복 코드를 없애기 위한 전략으로 만들어졌다.

## 마무리

코드의 품질은 무엇을 구현했는가보다 어떻게 구현했는가에 달려 있다.

작은 함수, 명확한 이름, 일관된 처리 방식이 모여 클린 코드를 만든다. 이름 짓기가 어렵다면, 책임이 너무 많은 것일지도 모른다.

작게 나누고, 더 나은 코드를 고민하는 습관을 가져보자.
