본문 바로가기
BackEnd/Clean Code

Clean Code : 함수

by 12312121 2022. 1. 11.

1. 작게 만들어라
이건 로버트 C. 마틴의 생각이다 그의 40여년간 경험상 작게 만드는게 좋다고 확신한다.
나도 동의한다. 왜냐하면 함수가 길 수록 기억해야 할 것과 집중해야 하는 요소가 많아 져서 이해하기 어려워진다.

또한 짧으면 함수들을 나열한 것처럼 작성이 되는데, 함수는 동사나 동사구를 이름으로 쓰기까지 해서

작성 결과가 마치 이야기 책을 써놓은 것처럼 작성이 되서 이해하기 매우 숩다
2~4 줄인게 좋다고한다

이것을 위해 if, while, for의 코드블록은 한 줄이여야한다. 그리고 대게 거기서는 함수를 호출한다

2. 함수는 한가지만 해야한다.

그런데 정확히 한가지를 한다는 것의 기준이 무엇인가? 리턴만 해야하는가? 조건문을 하나만 써야하는가?
답은 추상화 수준이 하나여야 한다.
세부적인 기능은 전부 생략하고 대표적인 기능 하나만 수행한다는 것이다.
근본 목표와 세부사항을 섞지마라.
한번이라도 섞는다면 깨진 창문 주변은 더 더욱 훼손 되는 것처럼 자신도 모르게 더 많은 세부사항을 계속해서 추가 해나갈 것이다

그리고 코드는 마치 이야기를 들려주는 것처럼 한 함수 다음에는 추상화 수준이 한단계 낮은 함수가 와야한다

아래는 그것의 예시다.
A를 하려면 B를 해야한다
B를 하려면 C를 해야한다
C를 하려면 D를 해야한다

3. Switch 문
switch 문은 작게 만들기 어렵다. 기본적으로 N가지를 처리한다. 그래서 배제하고 싶지만 불행하게도 완전히 피할 수 없다. 물론 다형성을 이용하면 저차원 class에 switch문을 숨기고 절대 반복하지 않을 수 있다

예를들어 보겠다.

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


이 함수는 문제가 많다.
첫째, 함수가 길다
둘째, 한 가지 작업만 수행하지 않는다
셋째 SRP(Single Responsibility Principle)를 위반한다
넷째, OCP(Open Closed Princple)을 위반한다.
(새 직원 유형을 추가할 때마다 무한정 코드가 변경 되기 때문이다)
가장 큰 문제는 위 함수가 무한정 존재해서 사람의 뇌로는 감당 할 수 없는 상황까지 어느새 도달 할 수 있다는 것이다

이 문제를 해결한 코드가 아래의 것이다

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 Emplyee 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);
}
}
}



이렇게 하면 두번째 클래스가 3번째 클래스를 숨겨서 안복잡해 보인다.
물론 이렇게 하더라도 switch는 최대 한번만 써야한다.

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

한가지 기능을 이름으로 해라. 그것만 봐도 그 함수의 기능 전부를 짐작할 수 있도록. 너무 길지 않나 하고 겁먹을 필요 없다. 짧고 어려운 이름 때문에 수십번 그 이름을 보면서 멈칫하고 시간을 오랫동안 낭비하는 것보단 백배 낫다.

이름을 정하느라 시간을 들여도 좋고 이런저런 이름을 바꿔 넣어보면서 무엇이 좋은지 생각해봐도 좋다. 최신의 ide에서는 한번의 클릭만으로도 같은 이름의 모든 함수의 이름을 바꿀 수가 있다.

includeSetupPage가 좋은 예이다

5. 함수 인수의 개수

함수에서 이상적인 인수의 개수는 0개지만
정말 어쩔 수 없이 있어야 하는 경우는 1 > 2 > 3 순으로 최대한 인수의 개수를 줄여야한다. 4개 이상은 어떠한 경우에도 하지마라. 지옥 시작이다.

코드를 읽는 사람은 함수의 이름만 보고 바로 넘어갈 수 있지만 인수까지 존재하는 경우 함수의 이름과 코드들을 분석해서 그 의미를 볼 때마다 유추해야한다.

물론 좌표에 관한 함수의 경우에는 인수가 두개인 것이 자연스럽고 오히려 한개면 이상하다. 이렇게 당연한 경우는 그렇게 써야한다

아래와 같이 3개의 인수를 2개로 줄이는 법도 있다.
Circle makeCircle(double x, doulbe y, double radius);
Circle makeCircle(Point center, double radius);
속임수 같아 보이지만 더 이해하기 쉬우면서도 생각할 것의 개수가 줄어들고 본래 기능은 하니 괜찮다.

6. 플래그 인수

이것은 대놓고 이 함수는 여러가지 기능을 수행한다는 의미이다. 왜냐하면 진실일땐 무얼하고 거짓일땐 무얼한다는 의미이기 때문이다. 자제해야한다

7. 명령과 조회를 분리하라

함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야한다. 둘다 하면 혼란을 초래한다 그러니까 함수는 객체 상태를 변경하거나 아니면 객체 정보를 반환하거나 둘중 하나만 해야한다.

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

오류코드 반환 방식은 7. 명령/조회 분리 규칙을 미묘하게 위반한다. 오류 코드를 반환하는 명령을 수행하면 오류 코드를 곧 바로 처리해야만 해서 여러 단계로 중첩되는 코드를 만들어야만 한다.

하지만 예외를 사용하면 원래 코드에서 분리되므로 깔끔해진다.

9. Try Catch 블록은 함수로 바꾸기

Try Catch는 정상동작과 오류 동작을 섞어버려서 혼란스럽다 그래서 try/catch 블록은 별도 함수로 바꾸는 편이 좋다

오류 처리도 한가지 작업이다. 그런데 함수는 한 가지 작업만 해야 되므로 오류 처리 함수도 오류만 처리해야한다.

그리고 오류를 정의할 땐 기존 오류 코드를 재사용하는 편이 좋다.

10. 반복하지 마라

중복되면 작업량이 그만큼 배로 증가하고 오류 발생률도 배로 증가한다

새 함수로 만들던가 해서 없애야한다.

11. 구조적 프로그래밍

모든 입력과 출력이 하나여야만 한다는 에츠허르 데이크스트라라는 말과 달리.
return, break, continue는 작은 함수의 경우엔 여러 차례 써도 된다. 하지만 goto는 큰 함수에서만 의미가 있으므로 작은 함수에서는 피해야한다.

12. 함수는 어떻게 짜는가?

글쓰기와 비슷하다 먼저 생각을 기록한 후 읽기 좋게 다듬는다.
함수를 짤 때도 마찬가지로 처음에는 길고 복잡하고, 이름은 즉흥적이고 코드는 중복된다. 하지만 이것 또한 빠짐없이 테스트하는 단위 테스트 케이스도 만든다

그다음 코드를 다듬고 중복을 없애고 메서드를 줄이고 순서도 바꾼다. 그러면서도 항상 단위 테스트를 통과한다. 그러면서 결국 Clean Code 규칙을 따르는 코드가 완성 된다.

처음부터 완벽하게 탁 짜지지는 않는다. 그런 사람은 없을 것이다

결론 : 함수는 언어의 동사이며 클래스는 명사이다.
프로그램은 구현할 것이 아니라 풀어갈 이야기로 여겨야 한다는 의미이다.


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

Clean Code : 추상화  (0) 2022.01.18
Clean Code : 주석2  (0) 2022.01.12
Clean Code : 주석  (0) 2022.01.12
전반적인 코드 작성 시에 써야할 것  (0) 2022.01.05
전반적인 코드에서 배제할 것  (0) 2022.01.04

댓글