본문 바로가기
Java/Clean Code

[클린코드(CleanCode)] 3장. 함수

by 잭피 2020. 11. 18.

작게 만들어라

함수를 만드는 규칙은 작게!

블록과 들여쓰기

if, else, while 문 등으로 들어가는 블록은 한 줄이어야 함 (대게 거기서 함수를 호출)

→ 그래야 함수는 읽고 이해하기 쉬워짐

한가지만 해라

한 가지만 해야하고, 한 가지를 잘해야함!

단순히 다른 표현이 아닌, 의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 작업을 하는 것

'한 가지'만 하는지 판단하는 방법

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

함수가 확실히 한 가지 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 함

한 함수 내에 추상화 수준을 섞으면 코드를 읽는 사람이 헷갈림

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

위에서 아래로 프로그램을 읽으면 함수 추상화 수준이 한 단계씩 낮아짐

Switch 문

switch 문은 작게 만들기 어렵고 한 가지 작업만 하기 어렵다

public Money calculatePay(Employee e) throws InvalidEmployeeType {
	switch(e.type) {
		case COMMISSTIONED:
			return calculateCommissionedPay(e);
		case HOURLY:
			return calculateHourlyPay(e);
	}
}
문제점
1. 함수가 길고, 추가하면 더 길어짐
2. 한 가지' 작업만 수행하지 않음
3. SRP 위반 (코드 변경 이유가 여럿)
4. OCP 위반 (직원 유형 추가 시, 코드 변경)
-> 가장 심각한 문제는 위 함수와 구조가 동일한 함수가 무한정 존재 (calculatePay, isPayday, deliverPay~)

switch 문은 다형적 객체를 생성하는 코드안에서만 사용하자! (저차원 클래스에 숨기고 반복하지 않는 방법)

switch 문을 추상 팩토리에 숨기고, 팩토리는 switch 문을 사용해 적절한 Employee 파생 클래스의 인스턴스를 생성함 (isPayday, deliverpay 등과 같은 함수는 Employee 인터페이스를 거쳐 호출)

public abstract class Employee {
	public abstract boolean isPayday();
	public abstract Money calculatePay();
	public abstract void deliverPay(Money pay);

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

public EmployeeFacotryEmpl implements EmployeeFacotry {
	public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType {
	switch(r.type) {
			case COMMISSIONED:
				return new CommissionedEmployee(r);
			case HOURLY:
				return new HourlyEmployee(r);
			....
		}
	}
}

서술적인 이름을 사용하라

이름이 길더라도 이름이 짧고 어려운거 보단 길고 서술적인 이름이 좋음

일관성 필요 (모듈 내 함수 이름이 같은 문구, 명사, 동사를 사용)

ex) includeSetupAndTeardownPages, includeSetupPage..

함수 인자

함수의 이상적인 인수 개수는 0,1,2 (3개는 피하는 편이 좋고, 4개 이상은 특별한 이유가 없는한 쓰면 안됨)

플래그 인수는 추하다

→ 함수가 한꺼번에 여러 가지 일을 한다고 대놓고 공표!

 

단항

  1. 인수에 질문을 던지는 경우 ex) fileExists("MyFile")
  2. 인수를 뭔가로 변환해 결과를 반환 ex) InputStream fileOpen("Myfile")
  3. 이벤트 함수 ex) passwordAttempFailedNtimes(int attempts) → 함수 호출을 이벤트로 해석해 입력 인수로 시스템 상태를 바꿈

→ 이 3가지 이외에는 단항 함수는 가급적 피하도록

 

이항

직교 좌표계 점은 일반적으로 인수 2개를 취하므로 적합

무조건 이항 함수가 나쁘다는 소리는 아니지만, 단항 함수로 바꾸도록 애쓰자

 

삼항

신중히 고려, 권고

 

부수 효과를 일으키지 마라

함수에서 한 가지만 하겠다고 약속하고선 남몰래 다른 것을 하지 마라 (세션 초기화 , 전역변수 수정 등)

 

명령과 조회를 분리하라

함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야한다

public boolean set(String attribute, String value);
if(set("username", "unclebob"))..
-> 함수 호출 코드의 의미가 모호...

-> 명령과 조회를 분리
if(attributeExists("username") {
	setAttribute("username", "unclebob");
}

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

오류 코드로 분기처리 하는 거보다 예외를 사용하면 코드가 분리되어 깔끔해짐

 

Try/Catch 블록 뽑아내기

try/catch 블록은 원래 추하다 (코드 구조에 혼란을 일으키고 정상 동작과 오류 처리 동작을 뒤섞음)\

try/catch 블록은 별로 함수로 뽑아내는 편이 좋음

 

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

오류 처리하는 함수는 오류만 처리해야 마땅함

즉 함수는 try로 시작해 catch/finally문으로 끝나야함

 

함수를 어떻게 짜죠?

처음엔 길고 복잡, 중복도 많고, 이름도 즉흥적

서투른 코드를 빠짐없이 테스트하는 단위 테스트 케이스를 만들고,

서서히 코드를 다듬고, 함수를 만들고, 이름을 바꾸고, 중복을 제거

때론 클래스를 쪼갬 (→ 변경 중에 항상 단위 테스트는 통과)

최종적으로 원하는 함수가 얻어짐

 

 

이 글은 “클린코드” 책 내용을 정리한 글입니다.

만약 저작권 관련 문제가 있다면 “shk3029@kakao.com”로 메일을 보내주시면, 바로 삭제하도록 하겠습니다.

 

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

[클린코드(CleanCode)] 4장. 주석  (0) 2020.11.18
[클린코드(CleanCode)] 2장. 의미있는 이름  (0) 2020.09.04

댓글