티스토리 뷰
함수
함수화 기술은 보수성 높은 코드를 작성하는 기본이다. 복잡한 코드도 함수화가 잘 되어 있으면 쉽게 보수할 수 있다.
함수의 기본 원칙
함수를 만들 때 도움이 되는 기본 원칙이 있다.
- 1개의 함수에는 1개의 역할만 한다.
- 함수를 두 종류로 구분한다.
1개의 함수에는 1개의 역할만 한다.
함수는 1개의 역할만 수행해야 한다. 이름이 복잡한 함수는 여러가지 역할을 할 가능성이 높다. 초보자는 기능이 많은 함수일수록 좋다고 생각하는 경우가 있다. 하지만 기능이 적은 함수일수록 재사용하기 쉽고 변경에 대한 영향도 적게 받는다. 결과적으로 보수성이 높이진다.
함수를 두종류로 구분한다.
함수는 다음과 같이 두종류로 구분된다.
- 첫번째: 계산과 알고리즘을 실행하고 실제 작업을 수행하는 함수
- 두번째: 첫번째 함수들을 조합해서 흐름을 만드는 함수
만약 이동과 충돌판정을 같이 수행하는 함수가 있다면 각 부분(이동, 충돌판정)을 함수(이동함수, 충돌판정함수)로 만들고, 이동함수와 충돌판정함수를 사용해서 함수를 만들어야 한다. 큰 함수를 제대로 작성하는 것은 어렵지만, 작은 함수를 제대로 작성하는 것은 쉽다. 큰일을 하는 복잡한 함수는 작은 일을 하는 단순한 함수로 분활해서 작성하자.
함수화 패턴
함수화하는 방법에도 패턴이 있다.
- 조건식 함수화
- 계산식 함수화
- 조건 분기의 블록 내부 함수화
- 반복문 함수화
- 반복문의 블록 내부 함수화
- 데이터 변환 함수화
- 데이터 확인 함수화
- 배열 접근 함수화
- 주석 부분 함수화
조건식 함수화
if 조건문의 조건식을 함수화 한다. 함수화하면 조건식에 의미 있는 이름을 붙일 수 있게 된다.
계산식 함수화
계산식을 함수화 한다. 계산식을 함수화하면 자연어를 읽는 것처럼 코드를 쉽게 읽을 수 있다.
조건 분기의 블록 내부 함수화
if 조건문 또는 switch 조건문의 블록 내부를 함수화한다.
반복문 함수화
1개의 반복문에 대해서 1개의 함수를 만든다. STL 알고리즘 함수를 이용하면 쉽게 반복문을 제거할 수 있게 된다.
반복문 블록 내부 함수화
for 조건문 등의 제어문 내부에 있는 if 조건문에는 조기 리턴을 사용할 수 없다. 하지만 반복문 블록 내부를 함수화한다면 조기 리턴을 사용할 수 있게 된다. 예를 들어 보자.
for (iter i = actors.begin(); i != actors.end(); ++i) {
if (i(*!)->is_dead()) {
if (player->state() == PLAYER_ATTACK) {
player->attack(**i);
}
}
}
위의 코드에서 for 반복문의 블록 내부를 함수화해보자.
void attack(Player& player, Actor& target) {
if (targer.is_dead()) return;
if (player.state() == PLAYER_ATTACK) return;
player.attack(targer);
}
for (iter i = actors.begin(); i != actors.end(); ++i) {
attack(*player, **i);
}
조기 리턴을 사용했지만 의미가 불명확하다. 이럴 때는 if 조건문을 함수화하고 이름을 붙여준다. 그리고 STL 알고리즘 함수를 사용해보자.
bool can_attack(Player& player, Actor& target) {
if (targer.is_dead()) return false;
if (player.state() == PLAYER_ATTACK) return false;
return true;
}
void attack(Player& player, Actor& target) {
if (!can_attack(player, target) return;
player.attack(targer);
}
std::for_each(actors.begin(), actors.end(),
[&](Actor* target) { attack(*player, *target); });
데이터 변환 함수화
데이터 변환은 if 조건문을 사용하거나, 변환 전용 자료 구조를 사용한다. 따라서 이러한 부분을 함수화하면 코드가 단순해져 가독성이 높아진다.
데이터 확인 함수화
데이터 확인 작업도 함수화시키면 좋다. 데이터 확인 부분에서도 여러개의 if 조건문과 확인 전용 자료 구조가 사용되기 때문이다.
배열 접근 함수화
1차원, 2차원 배열에 접근할 경우에는 배열에 접근하는 함수를 따로 만들자. 배열 범위 외부를 참조하지 않게, assert로 인덱스가 범위 내부에 있는 확인하는 처리를 추가하자. STL의 vector 또는 C++11에 추가된 array클래스를 이용하면 Debug 모드 때에 인덱스 범위 체크를 자동으로 수행하므로 더 편리해진다.
주석 부분 함수화
코드가 길어져서 주석을 써야 하는 경우가 생긴다. 그 순간이 함수화를 해야 하는 때이다. 주석을 쓰는 것을 좋은 습관이지만, 함수의 이름으로도 주석을 대신할 수 있다.
매개 변수가 너무 많은 문제
함수의 매개 변수가 많아지는 원인은 대부분 설계 문제이다. 반대로 함수의 매개변수를 줄인다는 것은 설계를 개선하는 작업이라 할 수 있다.
욕심쟁이 함수
1개의 함수가 너무 많은 일을 하면 매개변수의 수도 많아진다. 기능을 단순하게 함수를 분활하면, 매개 변수의 수도 줄어 든다. 리턴 값이 2개 이상인 함수를 되도록 만들지 말자. 리턴값이 2개 이상이라면 2개 이상의 기능이 있을 가능성이 높다.
매개 변수를 클래스화 시켜라.
매개 변수가 너무 많다면 클래스화하라는 신호가 될 수 있다. 연관이 있는 매개변수들을 클래스화하면 매개변수를 줄일 수 있다. 매개변수는 0~2개가 가장 이상적이다.
작은 함수의 필요성
함수화 패턴에 따라 코드를 함수화하면 작은 함수가 굉장히 많아진다. 왜 작은 함수를 만들어야 할까?
자기 설명적인 코드
코드를 작게 함수화하면 함수의 이름만으로 주석을 대신하게 되어서 읽기 쉬운 코드가 된다. 코드의 최소 단위를 함수로 생각하는 마인드를 가지다. 복잡한 함수는 작은 함수를 조합해서 만들어 진다.
개별 테스트 가능
함수화하면 개별적인 단위 테스트가 쉬워진다. 테스트에서 정상적으로 작동하는 함수들이 모여서 복잡한 함수가 되어도 신뢰성이 높아 진다.
작은 함수로 인한 실행 속도 저하
작은 함수로 분할하면 함수 호출 오버헤드로 실행 속도 저하가 일어나서 성능이 저하된다. 하지만 release 모드에서는 작은 함수는 인라인으로 전개된다. 따라서 인라인이 전개되면 함수 호출의 오버헤드가 없어진다.
실행 속도를 올리려면 알고리즘 또는 자료 구조 차원의 개선을 하라. 대폭적인 속도 향상을 원할 때는 멀티코어를 활용하는 구조로 설계하는 편이 더욱 좋다. 세세한 코드 레벨의 최적화는 컴파일러에게 맡기는 것이 좋다.
함수화 목적은 재사용뿐만이 아니다.
초보자는 재사용하지 않는 부분을 함수화하는 것에 대한 거부감을 느낄 수 있다. 하지만 함수화는 재사용성과 코드의 가독성을 높이고 aseert로 오류를 체크할 수 있으며 단위테스트도 쉬워져서 꼭 재사용만으로 함수화 시키는 것이 아님을 기억하자.
함수를 사용할 때의 마음가짐
처음부터 좋은 함수를 작성할 수는 없다. 일단 다른 사람에게 코드의 의미를 제대로 전달할 수 있게 노력해보자.
일단 작성해보기
우선 자신이 해결해야 할 문제를 정확하게 이해하는 것이 중요하다. 하지만 처음부터 문제를 제대로 해결할 수 없는 경우가 많다. 그래서 일단 코드를 작성하다 보면 해결하려는 문제에 관한 이해가 깊어지고 어떤 것을 작성해야 좋을지 더욱 명확해진다.
처음 작성했던 함수는 밑그림
시행 착오를 거치며 썼던 함수는 밑그림이라고 생각하자. 밑그림을 모두 그리고 나면 그때부터 본격적으로 함수를 제대로 작성해 보자. 시간이 흐르면 코드를 다시 떠올리기 힘들기 때문에 밑그림 상태에서 빨리 수정하는 것이 좋다.
코드가 가지는 의미를 제대로 이해
함수화를 시키면 코드가 가지는 의미를 제대로 이해하게 되고 잘못된 부분을 쉽게 이해할 수 있게 된다.
읽는 사람의 관전에서 확인
완성된 함수를 다시 한번 읽어보자. 남이 읽을 때도 잘 이해할 수 있는 함수가 좋은 함수이다. 미래에 변경 또는 추가가 있을 때 수정이 쉬운지 생각해보자.
'객체지향 설계 > C와 C++ 게임코드로 알아보는 코딩의 기술' 카테고리의 다른 글
객체 지향 설계의 기본 (0) | 2018.08.30 |
---|---|
클래스를 만드는 요령 (0) | 2018.08.30 |
switch 조건문의 감축과 단순화 (0) | 2018.08.30 |
for 반복문 감축과 단순화 (0) | 2018.08.30 |
if 조건문 감축과 단순화 (1) | 2018.08.30 |