본문 바로가기

Book Review/Clean Code

클린 코드 3장 정리 #1

1. 함수를 ‘작게’ 만들고, ‘더 작게’ 만들어라.

  • 함수는 얼마나 짧아야 좋을까? - 최대한 간결하게 나타내고 모든 함수가 명백한게 좋다.
  • if문/else문, while문 등에 들어가는 블록은 한 줄이어야 한다는 의미이다. 대개 거기서 함수를 호출한다.
  • 함수에서 들여쓰기 수준은 1단이나 2단을 넘어서는 안된다. 그래야 함수는 읽고 이해하기 쉬워진다.

2. 함수는 한 가지를 잘해야한다.

  • 함수는 한 가지를 해야한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야 한다.
  • 지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면, 그 함수는 한 가지 작업만 한다.
  • 함수가 ‘한 가지’만 하는지 판단하는 방법은 단순히 다른 표현이 아니라 의미 있는 이름으로 다른 함수를 추출할 수 있다면, 그 함수는 여러 작업을 하는 셈이다.
  • 한 가지 작업만 하는 함수는 자연스럽게 섹션으로 나누기 어렵다.

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

  • 한 함수 내에 추상화 수준을 섞으면 코드를 읽는 사람이 헷갈린다. 특정 표현이 근본 개념인지 아니면 세부사항인지 구분하기 어려운 탓이다. 하지만 문제는 근본 개념과 세부사항이 뒤섞기 시작하면, 깨어진 장문처럼 사람들이 함수에 세부사항을 점점 더 추가하게 된다.

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

  • 코드는 위에서 아래로 이야기처럼 읽혀야 좋다. 한 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 온다. 이것을 내려가기 규칙이라고 부른다.

TO 설정 페이지와 해제 페이지를 포함하려면, 설정 페이지를 포함하고, 테스트 페이지 내용을 포함하고, 해제 페이지를 포함한다.

TO 설정 페이지를 포함하려면, 슈트이면 슈트 설정 페이지를 포함한 후 일반 설정 페이지를 포함한다.

TO 슈트 설정 페이지를 포함하려면, 부모 계층에서 “SuiteSetUp” 페이지를 찾아 include 문과 페이지 경로를 추가한다.

TO 부모 계층을 검색하려면,…

  • 추상화 수준이 하나인 함수를 구현하기란 쉽지않지만 매우 중요한 규칙이다.

5. Switch 문

  • Switch 문은 작게 만들기 어렵다. 또한, ‘한 가지’ 작업만 하는 switch 문도 만들기 어렵다. 하지만 각 switch 문을 저차원 클래스에 숨기고 절대로 반복하지 않는 방법은 있다.
public Money calculatePay(Employee e) throws InvalidEmployeeType{
	switch (e.type){
		case COMMISSIONED:
			return calculateCommisionedPay(e);
		case HOURLY:
			return calculateHourlyPay(e);
		case SALARIED:
			return calculateSalariedPay(e);
		default:
			throw new InvalidEmployeeType(e.type);
	}
}
  • 위 함수는 함수가 길고 ‘한 가지’ 작업만 수행하지 않는다는 문제점이 존재한다.
  • OCP(Open Closed Principle)을 위반한다. 세 직원 유형을 추가할 때 마다 코드를 변경하기 때문이다.
  • 심각한 문제라면, 위 함수와 구조가 동일한 함수가 무한정 존재한다는 사실이다.
public abstract class Employee {
	public abstract boolean isPayday();
	public abstract Money calculatePay();
	public abstract void deliveryPay(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 SalariedEmplouyee(r);
			default:
				throw new InvalidEmployeeType(r.type);
		}
	}
}
  • switch 문을 추상 팩토리에 꽁꽁 숨기고, 아무에게도 보여주지 않는다.
  • 팩토리는 switch 문을 사용해 적절한 Employee 파생 클래스의 인스턴스를 생성한다.
  • calculatePay, isPayday, deliveryPay 등과 같은 함수는 Employee인터페이스를 거쳐 호출된다.

6. 서술적인 이름을 사용하라

  • 함수가 하는 일을 좀 더 잘 표현하는 이름이 좋다.
  • 함수가 작고 단순할수록 서술적인 이름을 고르기도 쉬워진다.
  • 이름이 길어도 괜찮다. 길고 서술적인 이름이 짧고 어려운 이름보다 좋다.
  • 이름을 붙일 때는 일관성이 있어야 한다.

7. 함수 인수

  • 함수에서 이상적인 인수 개수는 0개이고, 그다음은 1개, 2개이고 3개는 가능한 피하는 편이 좋다.
  • 인수는 개념을 이해하기 어렵게 만든다.
  • 출력 인수는 입력 인수보다 이해하기 어렵다.
  • 최선은 입력 인수가 없는 경우이며, 차선은 입력 인수가 1개뿐인 경우다.

8. 많이 쓰는 단항 형식

  • 함수에 인수 1개를 넘기는 이유로 가장 흔한 경우는 인수에 질문을 던지는 경우이다.
    • boolean fileExists(”MyFile”)
  • 다른 하나는 인수를 뭔가로 변환해 결과를 반환하는 경우이다.
    • InputStreamf fileOpen(”MyFile”)은 String 형의 파일 이름을 InputStream으로 변환한다.
  • 이벤트 함수는 입력 인수만 있고, 출력 인수는 없다. 프로그램은 함수 호출을 이벤트로 해석해 입력 인수로 시스템 상태를 바꾼다.
    • passwordAttemptFailedNtimes(int attempts)
  • 지금까지 설명한 경우가 아니라면 단항 함수는 가급적 피하는게 좋다. 예를 들어서 변환 함수에서 출력 인수를 사용하면 혼란을 일으키고, 입력 인수를 변환하는 함수라면 변환 결과는 반환값으로 돌려준다.

'Book Review > Clean Code' 카테고리의 다른 글

클린코드 5장_형식 맞추기  (0) 2022.09.06
클린코드 4장_주석  (0) 2022.08.30
클린 코드 2장 정리 #2  (0) 2022.07.29
클린 코드 2장 정리 #1  (0) 2022.07.28
클린 코드 1장 정리  (0) 2022.07.28