첫 번째, 들여쓰기 최소화가 정말 가치있는 것인가? 만약 가치 있다면 논리적인 근거를 제시할 수 있는가?
두 번째, 진입점과 종료점을 각 하나씩만 유지해야 하는 것은 의미 있는 일인가?
세 번째, 긍정 조건과 부정 조건 중 어떤 것이 더 이해하기 좋은가? 만약 상황에 따라 적절히 사용해야 한다면 긍정 조건과 부정 조건을 선택하기 위해 사용하는 기준을 도출할 수 있을까?
최근 읽고 있는 켄트벡의 Implementation Patterns에는 위 논제에 대한 답변을 얻는 데 도움이 될만한 내용이 많이 담겨있었습니다. 저는 그동안의 저의 생각과, 최근 해당 책에서 얻은 지식들을 이용하여 위 세 가지 논제 중 첫번째 논제에 대한 저의 생각을 얘기해보고자 합니다.(참고로 Implementation Patterns의 Guard Clause와는 조금 다른 시점의 글입니다. 이 글은 이전 글에서 얘기했던 들여쓰기 최소화 자체에 집중한 글임을 밝힙니다)
들여쓰기를 한다는 것은 현재 들여쓰기 되고 있는 단락 위에 진입 조건이 존재한다는 것을 의미합니다. 조건문, 반복문, 메서드 등이 이러한 진입 조건이 될 수 있습니다.
만약 어떤 사람이 조건문에 속해 있는 하위 단락을 읽는다면, 하위 단락이 어떤 경우에 실행되는지를 '전제'로 인식하고 있어야 합니다. 그런데 이러한 전제가 많아지고 단락이 깊어지면 문제가 생깁니다. 아래 예제 코드는 이러한 문제를 잘 드러내고 있습니다.
void compute() {
Server server = getServer();
if (server != null) {
Client client = server.getClient();
if (client != null) {
Request current = client.getRequest();
if (current != null) {
processRequest(current);
}
}
}
}
void compute() {
Server server = getServer();
if (server == null)
return;
Client client = server.getClient();
if (client == null)
return;
Request current = client.getRequest();
if (current == null)
return;
processRequest(current);
}
Implementation Patterns P71에서 인용
첫 번째 코드를 보면 읽는 사람이 전제로 유지해야 할 조건이 많다는 것을 볼 수 있습니다. 'server가 null이 아니고, client도 null이 아니며, current도 null이 아니면' 이라는 전제 조건이 processRequest에 이르기 전까지 단계적으로 머리속에 유지 되어야 합니다. 켄트백은 이러한 형태의 전제 조건들에 대해 '집중해야 할 것에 집중하지 못하게 방해하는 것들'이라고 얘기합니다. 실제로 위와 같은 코드는 집중해야 할 것에 집중하기 어렵게 하며 만약 else까지 존재한다면 더욱더 어려운 상황이 만들어집니다. 결국 코드를 이해하기가 어려워지는 것입니다.
반면 두 번째 코드는 동일한 일을 하면서도 보다 이해하기 쉬운 코드입니다. 코드에 윗 부분에 조건에 안 맞으면 바로 종료하는 코드가 있음을 볼 수 있습니다. processRequest에 이르렀을 때 첫번째 코드와는 달리 머리속에 전제 조건으로 유지해야 할 것이 없어집니다. 왜냐하면 'server가 null이면 종료','client가 null이면 종료','current가 null이면 종료' 이렇게 세 가지 조건 단락이 독립적으로 분리되어 이미 실행되었기에 더 이상 앞에 코드에 대해서 신경쓰지 않아도 되기 때문입니다.
만약 사람의 두뇌가 스택(Stack) 형태의 기억을 잘 할 수 있다면 첫번째 코드도 큰 문제는 아닐 것입니다. 하지만 저의 경우 스택 형태로 무엇인가를 기억하는 것은 쉽지 않았습니다. 따라서 몇단계의 전제 조건을 가지는 코드를 보면 한 세번째 전제 조건을 보는 중, 첫번째로 보았던 전제 조건을 잊어버려 허둥되곤 했습니다. 사실 이 문제는 저만의 문제는 아닌 것 같습니다. ANT의 depends 기능을 지나치게 활용하여 만들어진 빌드 스크립트를 분석하는 일은 저뿐만 아니라 다른 사람들도 무척 어려워 하더군요.
저는 첫 번째 논제인 '들여쓰기 최소화가 정말 가치있는 것인가?'라는 부분에 대해 '들여쓰기 최소화는 충분한 가치를 가지고 있다'고 주장하고 싶습니다. 왜냐하면 많은 경우 들여쓰기 최소화를 통해 더 이해하기 쉬운 코드를 만들 수 있기 때문입니다.
2008/01/15 ~ 2008/01/17
WRITTEN BY
- 차민창
르세상스 엔지니어가 사회를 이끌어나가는 상상을 하며!
트랙백 0개
,
댓글 14개가 달렸습니다.
if (server == null)
return;
이거보다 어차피 터미네이트가 목적이면..
if (server == null) return;
일케 한줄로 끝내는게 보기 좋지 않나?? @.@
이건 뭐 내 취향..
저도 사실 그 취향이예요. 그런데 예제를 수정 없이 옮기려다보니 그렇게 되었네요.
앞으로 블로그 통해서 많은 소통 있으면 좋겠습니다.
두번째 코드처럼 로직을 단순화하니까 이해하기도 편하고,
버그가 숨을 수 있는 여지도 적어서 유지보수하기 위해 필요하다고 생각해요.
좋은 글 잘 읽었습니다. ^^
최근 작성하는 코드에 열심히 적용중인데 일단 감각적으론 무척 만족하고 있습니다.
if (server == null) return; <== 이렇게 쓰면 디버깅할 때 라인 잡기가 명확하지 않던데요. (VS2003 씁니다.)
전
if (server == null)
return;
요런 스타일이 낫더군요 ^^;
일단 생각나는 데로 짚어 보자면, 검사할 조건이 리소스와 관련된 것이라면, 리소스 반환 문제가 있겠습니다. 첫 번째 조건에서 a 리소스를 얻어서 실패했을 경우 return 한다고 치죠. 성공한다면 b 리소스를 얻어서 검사를 하고 실패하면 a 리소스는 반환하고 return 해야 합니다. 그렇지 않고 검사에 성공했다면 c 리소스를 얻어서 검사를 하고 실패하면 a와 b 리소스를 반환하고 .. 같은 코드가 중복이 되죠. 실수로 빼먹을 수도 있고요.
또 하나는 검사가 실패할 경우에 어떤 동일한 처리를 하고 싶다고 합시다. 예를 들어 로그를 남긴다고 치면, return 할 때마다 같은 코드를 넣어야겠죠?
.. 그런 등등의 이유로 저는 들여쓰기를 하고 있어요.
들여쓰기 최소화는 원칙까지는 아닌 것 같고 일반적인 경우에 좋다라는 권고 정도 될 것 같습니다. 따라서 들여쓰기 최소화가 힘든 곳에서 어렵게 들여쓰기 최소화를 할 필요는 없다고 생각하구요. :)
마지막으로 자원 해제나 로깅을 한곳에서 하면서 들여쓰기 최소화도 할 수 있는 방법이 있을 것이라 생각되네요.
기존의 코딩습관으로는 매우 어려운 연습이죠..그래도 한번 해보고자 합니다.