작게 만들어라
함수에 대한 클린코드의 메세지는 2장의 의미있는 이름을 만드는 방법과 연장선에 있는 내용이다. 더 편하고 이해하기 쉽게 코드를 작성하는 것을 목표로한다.
모든 프로그램의 가장 기본적인 단위는 함수이다. 기존의 코드를 읽을 때 함수부터 시작하게 되는데 함수가 조금이라도 긴 경우 읽고싶은 마음은 없어지고 스트레스만 밀려오게 된다.
함수는 짧을 수록 좋다는게 클린코드의 철학이다.
한 가지만 해라
대부분 길이가 긴 함수는 두가지 이상의 일을 하는 경우가 많다. 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다.
함수를 만드는 이유는 큰 개념을 다음 추상화 수준에서 여러 단계로 나눠 수행하기 위해서이다.
길이가 긴 A함수 안에서 의미 있는 이름의 다른 함수를 추출할 수 있다면 A함수는 여러 작업을 하는 셈이다.
함수당 추상화 수준은 하나로
함수가 한가지 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야한다.
한 함수 내에서 추상화 수준이 섞여있으면 코드를 읽는 사람이 헷갈린다.
예를 들어 map안에 있는 공을 좌우로 움직이는 함수가 있다고 해보자
추상화 수준이 섞여있는 코드의 예)
moveBall 안에서 하는 일을 정리해 보면
1) 현재 공의 위치를 찾고
2) map의 범위가 벗어나지 않는지 확인하고
3) destinationPosition의 위치를 계산하고
4) 현재 위치와 목적지의 값을 변경한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
int map[100];
void moveBall(int direction) {
int currentPosition = -1;
for(int i=0; i<100; i++) {
if(map[i]) {
currentPosition = i;
break;
}
}
if(currentPosition == -1)
return;
int destinationPosition = currentPosition + direction;
if(destinationPosition < 0 || destinationPosition > 100) {
cout<<"Out of Position"<<endl;
return;
}
map[currentPosition] = 0;
map[destinationPosition] = 1;
return;
}
|
cs |
클린코드의 철학에 입각해 위의 코드의 작게, 한가지 일만하면서 추상화 수준을 맞추면 아래와 같이 수정 할 수 있겠다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
int map[100];
void moveBall(int direction) {
int currentPosition = findCurrentBallPosition();
if(!isValidPosition(currentPosition))
return;
int destinationPosition = calculateDestinationPosition(currentPosition, direction);
if(!isValidPosition(destinationPosition))
return;
switchCurrentPostionToDestinationPosition(currentPosition, destinationPosition);
return;
}
int findCurrentBallPosition() {
int currentBallPosition = -1;
for(int i=0; i<100; i++) {
if(map[i]) {
currentBallPosition = i;
break;
}
}
return currentBallPosition;
}
bool isValidPosition(int ballPosition) {
if(ballPosition < 0 || ballPosition > 100) {
printError("Out of Position!");
return false;
}
return true;
}
void printError(char* msg) {
cout<<msg<<endl;
}
int calculateDestinationPosition(int currentPosition, int direction) {
return currentPosition - direction;
}
void switchCurrentPostionToDestinationPosition(int currentPosition, int destinationPosition) {
map[currentPosition] = 0;
map[destinationPosition] = 1;
}
|
cs |
위의 코드는 아래 그림 처럼 추상화 레벨을 나누어 개발한것이다.
함수 인수
함수에서 이상적인 인수 개수는 0개이다. 0개인 경우를 만들기는 쉽지 않다.
다음은 1개이고 그 다음 이상적인 것은 2개이다. 3개는 피하는 편이 좋고, 4개이상은 특별한 이유가 있어도 사용하면 안된다.
함수 인수가 많은 경우 인수를 1개로 만들 방법이 있는지 또는 객체로 캡슐화 할 여지가 있는지 생각해 본다.
예)
writeField(outputStream, name)
=> outputStream.writeField(name)
Circle makeCircle(double x, double y, double radius);
=> Circle makeCircle(Point center, double radius);
명령과 조회를 분리하라
함수는 어떤 일을 수행하거나(명령) 뭔가에 답하거나(조회) 둘 중 하나만 해야 한다.
한 가지만 하라라는 원칙과 일맥상통하는 내용이다.
예를 들어 해당 속성이 존재하면 이름을 설정하는 함수가 있다고 해보자
ex) boolean set(String attribute, String value);
if(set("username", "d-yong"))
으로 코드를 작성할 경우 다른 사람이 봤을 때 이해하기 어렵다.
따라서 아래와 같이 명령과 조회를 분리하여 작성한다.
if(attributeExists("username")) { // 조회
setAttribute("username", "d-yong")); //명령
}
소프트웨어를 짜는 행위는 글짓기와 비슷하다. 처음부터 완벽하게 만들 필요는 없다. 초안을 만들고 다듬어 가다보면 깨끗한 코드에 더 가까워질 것이다.
'좋은 코드 만들기 > 클린코드' 카테고리의 다른 글
[클린코드] 6. 객체와 자료 구조 (0) | 2021.04.25 |
---|---|
[클린코드] 5. 형식 맞추기 (0) | 2021.04.11 |
[클린코드] 4. 주석 (0) | 2021.03.28 |
[클린코드] 2. 의미있는 이름 (0) | 2021.03.06 |
[클린코드] 1. 깨끗한 코드 (0) | 2021.02.28 |