왜 프로그래머가 갶독성을 향상시키는 수련을 평생 해야 하는지를 알려준다. 단지 상투적인 이유만 들먹이는 게 아니다. 좋은 갶독성은 프로그래머를 프로로 만들어주고, 큰 기쁨을 주며, 성장할 기회를 준다고 역설한다. 갶독성을 향상시키려면 눈에 보이는 것들부터 신경을 써야 하며, 코드 자체가 프로그램을 설명해야 하며, 흐름을 단순화하고 주석을 잘 쓰고 퇴고해야 한다는 간단한 원칙부터 지켜나가야 한다.
16. 눈에 보이는 것
소중한 것들은 눈에 보이지 않는 것
들이야
-어린 왕자
눈에 보이는 것도 소중하단다.
-염산악
17. 레이아웃: 기본 중에 기본
좋은 구현, 충분한 주석에도 레이아웃이 엉망이면 이해 불가
실수를 막기 위한 규칙들
자동화된 도구로 강제 가능
18. 야심찬 도전과제
오른쪽의 코드를 우리 한 번 열심히 잘
이해하도록 해 봅시다.
하기 싫습니다.
하지 맙시다.
google.j.b=(!!location.hash&&!!location.hash.match('[#&]((q|fp)=|tbs
=rimg|tbs=simg|tbs=sbi)'))||(google.j.qbp==1);(function(){google.c={
c:{a:true,d:false,i:false,m:true,n:false}};google.sn='webhp';(functi
on(){function
f(a,b,c){g.push({o:a,v:b,w:c});a.addEventListener?a.addEventListener
(b,c,!1):a.attachEvent&&a.attachEvent("on"+b,c)}function
e(a,b,c){a.addEventListener?a.removeEventListener(b,c,!1):a.attachEv
ent&&a.detachEvent("on"+b,c)}var
g=[];google.timers={};google.startTick=function(a,b){var
c=b?google.timers[b].t.start:google.time();google.timers[a]={t:{star
t:c},e:{},it:{},m:{}};(c=window.performance)&&c.now&&(google.timers[
a].wsrt=Math.floor(c.now()))};google.tick=function(a,b,c){google.tim
ers[a]||google.startTick(a);c=c||google.time();b instanceof
Array||(b=[b]);for(var
d=0;d<b.length;++d)google.timers[a].t[b[d]]=c};google.c.e=function(a
,b,c){google.timers[a].e[b]=c};google.bit=function(a,b){google.timer
s[a]||google.startTick(a);var
c=google.timers[a].it[b];c||(c=google.timers[a].it[b]=[]);var
d=c.push({s:google.time()})-1;return
function(){c[d]&&(c[d].e=google.time())}};google.c.b=function(a){var
b=google.timers.load.m;b[a]&&google.ml(Error("a"),!1,{m:a});b[a]=!0}
;google.c.u=function(a){var
b=google.timers.load.m;if(b[a]){b[a]=!1;for(a in
b)if(b[a])return;google.csiReport()}else
google.ml(Error("b"),!1,{m:a})};google.rll=function(a,b,c){var
d=function(b){c(b);e(a,"load",d);e(a,"error",d)};f(a,"load",d);b&&f(
a,"error",d)};google.ull=function(){for(var
19. 실수 방지
빠르게 코드를 읽어봅시다.
다시 찬찬히 봅시다.
에너지가 더 소모됩니다.
사실 Submit은 한 번만 수행됩니
다.
에너지를 아낍시다.
for (i = 0; i < size; ++i)
if (a[i] > b[i]) {
a[i] = b[i];
last = i;
}
Submit(a);
return size;
20. 자동화된 도구
가능하면 자동화된 도구로 최대한
간단한 포매팅 문제를 해결합시다
.
for (i = 0; i < size; ++i) {
if (a[i] > b[i]) {
a[i] = b[i];
last = i;
}
}
Submit(a);
return size;
22. 덤: 읽기 쉬운 조건문
if (size < MAX_SIZE) { ... }
if (MAX_SIZE > size) { ... }
if (0 <= index && index < size) { ... }
if (index >= 0 && index < size) { ... }
23. 코딩 컨벤션
유명한 코딩 컨벤션들을 답습하기
언어에서 권장하는 컨벤션 익히기
주변의 코드들과 일관성 유지하기 (개성을 죽이는 교육)
24. 문단 나누기
관련 있는 것들끼리 뭉치자. 줄을 맞추자.
=> 루틴이 하는 일을 하나로 하자.
=> 클래스에 연관있는 메소드들만 있게 하고 더 가까이 관련 있는 것들끼리 묶자.
=> 파일 이름 잘 짓고 끼리끼리 묶자.
25. 클래스/파일/패키지 이름
클래스, 파일, 패키지 이름이 그냥 Util, utils.cc, helper.cc 같은 것이고 수많은 루
틴들이 들어가 있지는 않는가? What about foobar, then?
의미 있는 것들끼리 모으고 묶고 이름을 짓자. 이름이 지어지지 않으면 분리하자.
이 클래스가 무엇을 하는지, 너무 많은 것을 하지는 않는지 살피자.
26. 시대와 환경의 변화에 따른 권장 사항의 변화
강제적인 헝가리안 표기법
타입 검사가 잘 되지 않고 IDE 지원이 열악하던 시절 좋은 표기법이었음.
하나의 종료점
수동으로 메모리 할당과 해제를 하던 시절에는 유용한 규칙이었음.
GOTO exit;
스코프 등을 이용한 자원 획득/해제(RAII), with등으로 제약, try-finally로 해제 코드를 넣을 수 있
28. 코드가 말한다
아마추어의 코드에는 지나치게 주석이 많음.
줄 단위의 주석... 주석을 상세히 쓰라니 배운대로
열심히 i에 0을 넣는다고 주석 씀.
상세한 주석에도 불구하고 읽기는 매우 어려움.
읽어도 의도를 알기 어려움. (e.g. // Skip first
row)
// Remove first 6 characters
query = request[6:]
프로의 코드에는 주석이 적당함.
문서화에 필요한 주석이 대부분. 변수나 함수 이
름이 스스로 설명함.
주석이 거의 없지만 읽기는 매우 쉬움.
코드로 알기 어려운 의도 등을 주석으로 밝힘.
(e.g. // Skip header row)
const string pathPrefix = "/api/?";
query = request[len(pathPrefix):]
29. 함축과 압운: 언어 영역을 배운 이유
이름에 정보를 최대한 많이 넣어야 하지만 특히 긴 이름 사용이 반복되면 짜증난
다.
따라서 함축이 필요하다!
압운은 두운, 요운, 각운으로 나뉜다. 요~ 롸임 있는 코드.
max, min, num, cnt, hex, unsafe, enc, has, is, ...
30. 함수 이름
서브루틴은 호출하고 반환받을 수 있는 코드의 덩어리에 이름을 붙인 것.
이 이름을 잘 붙이는 것으로 독자들이 그 코드가 무엇을 하는지 더 잘 알 수 있다.
언어 영역/외국어 영역에서 배운 포괄하는 제목 짓기를 생각하자.
37. 압운
매개변수 이름을 잘 짓자. @Nullable 같은 것이 지원되지 않으면 OrNull, NotNull
을 변수 이름이나 함수 이름에 붙이면 더 읽기 쉬워지는지 살펴보자.
문자열은 경우에 OrEmpty 같은 것이 붙으면 더 읽기 쉬워지는지 살펴보자.
시간에 단위 _sec, _ms, _us, _ns 같은 것이 붙으면 더 읽기 쉬워지는지 보자.
폰트 크기가 font_size인 것보다 _pt, _px가 붙으면 도움이 되는지 보자.
font_size_px = PointToPixel(font_size_pt);
38. 변수 이름을 지어주세요
// If needs recalculation
if (xxx && yyy && zzz) {
... code ...
}
bool needs_recalc = xxx && yyy && zzz;
if (needs_recalc) {
... code ...
}
39. 과정 설명
여러 스텝으로 나눌 수 있는 함수
이미 이 정도라도 나눠진 코드를
기반으로 작업할 수 있는 것을 감
사하게 생각하자.
// Step 1. Read input.
... code ..
// Step 2. Generate results.
... code ...
... code ...
// Step 3. Update results.
... code ...
... code ...
// Step 4. Write output.
... code ...
40. 함수 이름으로
이름이 곧 어떤 순서로 무엇을 하
는지 설명한다.
ReadInput();
GenerateResults();
UpdateResults();
WriteOutput();
45. One Exit Result* result = nullptr;
for (int i = 0; i < n; ++i) {
Message msg = getMessage();
if (!msg.empty()) {
... actual code ...
result = msg.AsResult();
break;
}
}
return result;
46. Return Early
for (int i = 0; i < n; ++i) {
Message msg = getMessage();
if (!msg.empty()) {
... actual code ...
return msg.AsResult();
}
}
return nullptr;
47. 루프와 함수 분리
기억해야 할 변수도 줄어든다.
루프가 무엇을 하는지가 분명해진다. (함수 이름을 잘 지으면...)
48. 루프와 함수 분리
루프와 상태 변수를 이용해야 하
는 경우가 있다면 함수를 분리하
면 더 읽기 쉬워지는지 살펴보자.
이번에도 이미 이런 코드를 갖고
작업할 수 있음을 감사하게 생각
하자.
bool found = false;
for (const auto& item : items) {
if (...) {
found = true;
break;
}
}
if (found) {
...
}
49. 루프와 함수 분리
return을 이용하면 코드가 간단함.
물론 functional한 함수들을 이용
하는 방법도 있겠음.
bool ContainsItem(...) {
for (const auto& item : items) {
if (...) {
return true;
}
}
return false;
}
if (ContainsItem(...)) {
...
}
50. GOTO와 break label이 사용되는 경우
여러 루프를 빠져나와야 하는 경우 break label을 쓰거나 조건 변수를 써야 하는
경우
함수를 분리하면 (혹은 클로저를 쓰면) 더 읽기 쉬워지는지 살펴보자.
return은 함수 안에 있는 모든 루프를 한 번에 빠져나오므로...
51. 루프와 함수 분리
goto를 쓰거나 break label을 써야
한다.
코드가 길어지면 흐름이 더욱 복
잡해진다.
bool found = false;
for (const auto& item : items) {
for (const auto& attr: item.attrs()) {
if (...) {
found = true;
... (1) code ...
goto outer; // break outer;
}
}
}
outer:
if (found) {
... (1) code ...
}
... (2) more code ...
52. 루프와 함수 분리
return을 이용하면 코드가 간단함.
물론 functional한 함수들을 이용
하는 방법도 있음.
bool ContainsItem(...) {
for (const auto& item : items) {
for (const auto& attr : item.attrs())
{
if (...) {
return true;
}
}
}
return false;
}
if (ContainsItem(...)) {
... (1) code ...
}
... (2) more code ...
53. 예외 상황 쳐 내기
예외 상황을 안고 코드를 읽을 때 심란해진다.
일찍 쳐 내면 이후의 코드가 간단해지는 경우가 많다.
54. One Exit Result* result = nullptr;
if (condition) {
Message msg = getMessage();
if (!msg.empty()) {
... some code ...
... some code ...
result = msg.AsResult();
}
}
return result;
55. Return Early if (!condition) {
return nullptr;
}
Message msg = getMessage();
if (msg.empty()) {
return nullptr;
}
... some code ...
... some code ...
return msg.AsResult();
56. Return Early
이 부분이 초기 조건 검사 부분이
된다.
if (!condition) {
return nullptr;
}
Message msg = getMessage();
if (msg.empty()) {
return nullptr;
}
... some code ...
... some code ...
return msg.AsResult();
57. 예외 조건 검사
조건없이 묻지도 따지지도 않고...
- 보험/대부 업계
저기 저 그렇게 쉬운 사람 아니거든
요?
- 염산악
58. Design by Contract (DbC)
추상 자료형에 다음을 덧붙임
precondition
invariant
postcondition
60. Defensive Programming vs DbC
방어 운전에서 따온 것으로, "입력이
무엇이 들어올지 알 수 없으니, 입력을
믿지 말고 조심해서 어떻게든 잘 가 봅
시다." 와 같은 자세
가능하면 죽지 않고 감.
어떤 입력이 들어와야 하는지 상세히
명시하고 잘못된 입력이 들어오면, "계
약 위반이오! 그러지 않기로 했잖소!"
하면서 죽어버리는 자세
프로덕션 코드에서는 성능을 위하여
검사하지 않기 때문에 위반 시 정의되
지 않은 행동을 하게 됨
61. Precondition
조건이 만족하지 않을 때, 로그를
남기고 에러를 리턴하거나, 죽어
버리거나 할 수 있다. 디버그 모드
인지 릴리즈 모드인지에 따라 달
라질 수 있다.
if (!Precondition(src)) {
// ...
}
for (i = 0; i < size_; ++i) {
if (min_[i] > src[i]) {
min_[i] = src[i];
last = i;
}
}
return last;
69. 보고 드리겠습니다!
코드를 하나 하나 설명하지 않는다. 이런 걸로 영어 공부 하고 싶어도 참는다.
필요 없는 주석은 지운다. 영어로 써 놓은 주석이 아까워도 할 수 없다.
70. 보고 드리겠습니다!
코드를 하나 하나 설명하지 않는다. 이런 걸로 영어 공부 하고 싶어도 참는다.
필요 없는 주석은 지운다. 영어로 써 놓은 주석이 아까워도 할 수 없다.
슬라이드에 필요 없는 내용들도 좀 지우시지?
71. 상세한 것만이 능사가 아니다
길게 쓰는 것보다 단어 하나의 파워가 더 강력할 수 있다.
// 이 함수들은 따로 하는 일에 대한 구현이 들어 있는 것이 아니라 좀 더 쓰기 어
려운 복잡한 함수를 호출하기 위한 준비 작업을 하고, 매개 변수의 순서도 우리
프로젝트 표준에 맞게 바꾸어서 구현이 들어 있는 함수들을 호출해 준다.
72. 상세한 것만이 능사가 아니다
길게 쓰는 것보다 단어 하나의 파워가 더 강력할 수 있다.
// 이 함수들은 따로 하는 일에 대한 구현이 들어 있는 것이 아니라 좀 더 쓰기 어
려운 복잡한 함수를 호출하기 위한 준비 작업을 하고, 매개 변수의 순서도 우리
프로젝트 표준에 맞게 바꾸어서 구현이 들어 있는 함수들을 호출해 준다.
// 이 함수들은 래퍼들이다.
73. 어떤 것을 쓸까요?
왜 이렇게 했는가?
TODO, FIXME, ...
버그 같아 보이지만 버그가 아님, 핵 구현, 속도 때문에 정확도를 조금 버림, 이 방
법보다 그 방법이 더 빠를 거라 생각하겠지만 이게 더 빨랐음.
커맨드라인 사용 예제
74. 정리
레이아웃 및 형식화는 자동화된 도구와 열정으로. 그리고 일관성을 유지하자
의미 있는 것들끼리 모아서 추상화하자
변수/함수 이름만 잘 붙여도 갶독성이 증가한다
흐름을 단순화하자
주석을 잘 쓰고 퇴고하자.