본문 바로가기

회고/IT도서

읽기 좋은 코드가 좋은 코드다

반응형

 

코드는 이해하기 쉬워야 한다.

 

코드는 다른 사람이 그것을 이해하는 데 들이는 시간으 최소화하는 방식으로 작성되어야 한다.

 

Size() -> Height(), NumNodes(), MmoeryBytes()

Stop() -> Kill() , Resume(), Pause()

getPage(0 -> FetchPage((), DownloadPage()

 

retval, tmp -> sum_squares()
위와 같은 단어는 짧게 임시적으로 쓸때만 사용한다.

 

sum_squares += v[i] //더해야 하는 제곱은 어디에 있다는 말인가? 버그이다!

String tmp = user.name(); // tmp -> user.info

 

루프반복자

i,j.k -> clubs_i, members_i, users_i (ci,mi,ui)처럼 명확한 의미!

 

TCP/IP

ServerCanStart()->CanListenOnPort() 추상적이기보다 구체적으로 이름을 선언하라

 

start -> start_ms (getTime()이 second가 아니라 millisecond를 반환하기 때문에 오류날 가능성이 있다.)

 

클래스 멤버변수를 로컬 변수와 구분하고 싶다면 offset_처럼 _를 만들어라

 

jQuery에서,

ar $all_images = $("img")라고 하면 $all_images가 JQuery임을 알 수 있다.

 

HTML에서 밑줄로 id 안에 있는 단어를 구분하고, 대시로 클래스 안에 있는 단어를 구분한다.

<div id="middle_column" class="main-content>

 

CART_TOO_BIG_LIMIT -> MAX_ITEMS_IN_CART 

한계를 설정하는 이름을 가장 명확하게 만드는 방법은 max_ , min_을 붙이는 것이다.

 

start , stop -> first, last

 

begin / end에서 begin은 시작을 의미하지만 end는 끝이 포함되지 않는 경우이다.

 

이름에서 의미를 부정하는 용어를 피하는 것이 좋다.

bool read_password -> need_password or user_is_authenticated

disable_ssl -> use_ssl

 

오래걸리는 연산

getMean() -> computeMean()

 

단순 size를 보여주는건지 계산하는것인지?

list.size ->countSize or counterElementes

 

주석도 미학적으로 보기 좋게 쓸 수 있다.

// -----------

// -----------

class SatatsKeep{

  void Add(double d);

}

 

 

// -----------

class SatatsKeep{

  // -----------

  void Add(double d);

}

 

예를 들어, 생성자를 만들 때 각각의 생성자에 다 주석을 달지 말고 예시를 보여준다.

 

// TcpConnectionSimulater ( 처리량, 지연속도, 흔들림, 패킷_손실)

//                                    [Kbps]    [ms]       [ms]      [percent]

 

 

assert를 쭉 늘여서 쓰지 말고 이렇게 메소드로 묶어서 불규칙성을 정리하라

CheckFullName( a, b, ,c, d);

CheckFullName( a, b, ,c, d);

CheckFullName( a, b, ,c, d);

CheckFullName( a, b, ,c, d);

 

void CheckFullName( ...) {

 ....

}

 

메소드가 여러개 일 때 논리적 영역을 따라 여러 개의 그룹으로 나눈다.

//핸들러들

void ...

void ...

void ...

 

// 질의/응답 유틸리티

void ...

void ...

void ...

 

의미없고 무가치한 주석을 달지 않는다. (예를 들어 Node에 관하여)

Node* FindNodeInSubtree(Node* subtree, string name, int depth);

 

// 주어진 이름과 깊이를 이용해서 서브트리[h1]에 있는 노드를 찾는다.(x)

 

//주어진 'name'으로 노드를 찾거나 아니면 NULL을 반환한다.(o)

//만약 depth<=0이면 'subtree'만 검색된다.(o)

//만약 depth == N 이면 N 레벨과 그 아래만 검색된다.(o)

 

나쁜 이름에 주석을 달지 말고 대신 이름을 고쳐라

CleanReply -> EnforceLimitsFromRequest

DeleteRegistry -> ReleaseRegistryHandle

 

생각을 기록해라(감독의 설명을 포함하라)

// 이 데이터에서 이진트리는 해시테이블보다 40%정도 빠르다

//해시를 계산하는 비용이 좌/우 비교를 능가한다

 

주석으로 코드가 훌륭하지 않은 이유도 설명할 수 있다.

//이 클래스가 점점 엉망이 되어가고 있다. 'ResoureNode' 하위클래스를 만들어서 정리해야 할 수도 있다.

 

상수에 대한 설명을 달자(사연)

//합리적인 한계를 설정하여라 - 2 * num_processors보다 크거나 같으면 된다.

const int MAX = 100

 

상수가 변경되지 않는게 더 좋은 경우도 있다.

image_quality = 0.72 // 사용자들은 0.72가 최선이라고 생각한다.

 

나올 것 같은 질문 예측하기(C++)

//벡터가 메모리를 반납하도록 강제한다 ("STL swap trick"을 보라)

vector<float>().swap(data)

 

사람들이 쉽게 빠질 것 같은 함정을 경고하라

//외부 서비스를 호출하여 이메일 서비스를 호출한다(1분 이후 타임아웃된다.)

 

//실행시간이 0이므로 엉망으로 중첩된 입력을 사용할 때는 주의해야 한다.

 

큰 그림을 설명하는 방식도 좋다.

//고객이 자신을 위해서 구입한 항목을 모두 찾는다.

 

글쓰기 두려움을 떨쳐내라

//주의 : 이 코드는 리스트 안에 있는 중복된 항목을 다루지 않는다. 그렇게 하는 것이 어렵기 때문이다.

 

주석 문장 다듬기

//이 URL을 전에 이미 방문했는지에 따라서 다른 우선수위를 부여한다.

-> //전에 방문하지 않은 URL에 높은 우선순위를 부여한다.

 

함수의 동작을 명확히 설명하라

//이 파일에 담긴 줄 수를 반환한다.

-> // 파일 안에 새줄을 나타내는 바이트('\n')가 몇 개 있는지 센다.

 

입/출력 예를 사용하라

// 입력된 'src'의 'chars'라는 접두사와 접미사를 제거한다.

-> // 예 :Strip("abba/a/ba", "ab")은 "\a\"를 반환한다.

 

코드의 의도를 명시해라

//리스트를 역순으로 반복한다.

-> //각 가격을 높은 갑셍서 낮은 값 순으로 나타낸다.

 

이름을 가진 함수 파라미터 주석

Connect(10, flase)

-> Connect(/* timeout_ms =*/ 10, /*use_encryption =*/ false)

 

조건문에서 왼쪽은 유동적인 값이 온다

while( bytes_received > bytes_expected)

 

if(!debug)가 아닌 if(debug)를 선호하라

처음에 긍정의 조건문을 시작하라

 

삼항 연산자는 매우 간단할때만 사용하라!

 

do/while 루프를 피하라 (코드를 2번 읽기 때문)

 

함수 중간에 반환하기

자바 - try finally

파이썬 - with

C# - using

C++ - destructor

 

goto는 C를 제외하고 피하는게 좋다

if (p == NULL) goto exit:

 

exit:

 fclse(file1);

 ...

 return;

 

코드 중첩을 최대한 피하라

 

모든 if블럭은 return;으로 종료된다. (if - return)

if(user_result !=SUCCESS) {

  reply.Done();

  return;

}

 

중간에 반환하는게 아니라 계속해야한다면 continue를 사용한다 ( if - continue)

if(results[i] == NULL ) continue;

non_null_count++;

 

뒤에서 흐름을 따라가기 어려운것들이 차지하는 비율이 낮아야 한다.

스레딩 / 시그널, 인터럽트 핸들러 / 예외 /

함수포인터&익명 함수(함수가 런타임에 실행되어 컴파일 과정에서 어떤 코드가 실행되는지 모른다) /

가상메소드

 

===============================2/19(수)=============================

 

거대한 표현을 여러조각으로 나누자

if line.split(':')[0].strip() == "root": (x)

username = line.spit(':')[0].strip()  (o)

if username == "root":              (o)

    ...

 

*틀린 예시

if (request.user.id == document.owner_id) {

}

 

* 올바른 예시

final boolean user_owners_document = (request.user.id == document.ower_id);

if( user_owns_document ) {

}

 

드모르간 법칙 이용하기(and -> or)

if (!(file_exists && is_protected))

-> if (!file_exists \\ is_protetcted)

 

assert((!bucket = FindBucket(key))) \\ !bucket->IsOccupied());

-> bucket = FindBucket(key)

    if(bucket != NULL) assert(!bucket->IsOccupied());

 

거대한 구문 나누기

각각의 if문 안에 겹치는 구문은 따로 변수로 뺄 것

highlight가 중복적으로 나오면 오타가 나올 수 있으므로 string hi = "highlight 처럼 변수 선언

 

C++에서,

#define으로 반복되는 메소드 코드자체를 뺄 수 있다.

 

변수제거하기(직관적인 것은 그대로 사용)

 

*틀린 예시

now = datetime.datetime.now()

root_message.last_view_time = now

 

*올바른 예시

root_message.last_view_time = datetime.datetime.now()

 

중간결과 삭제하기

index_to_remove=i 로 중간과정을 거쳐 삭제하지 말고 array.splice(i,1) 사용

 

전역변수를 최대한 줄여라

많은 메소드를 정적 static으로 만들어서 클래스 멤버 접근을 제한해라

커다란 클래스를 여러 작은 클래스로 나누어라 클래스를 두개의 작은 클래스로 나누었는데

서로의 멤버를 참조한다면, 실제로 성취한 일은 아무것도 없게 된다.

 

class LargeClass{

 string str_;

 

 void Method1(){

  str_ = ...;

  Method2();

}

 

 

void Method1() {

  string str = ...;

  Method2(str)

}

 

void Method2(string str){

  //str를 이용한다.

}

 

함수 하나에서만 사용되는 전역 변수는 함수에 넣어서 사용하라 

 

변수가 많다면, 변수를 실제 사용하는 순서대로 정의하면 편하다.

int a

int b

int c

 

int a

...... 

......

 

int b

int c

.....

.....

 

=========================2/28(금)=================================

*코드 재작성하기

일반적인 목적의 코드를 프로젝트의 특정 코드에서 분리하라!

ex) cipher를 이용한 url 암호화는 url_safe_encrypt(user_info)의 함수에 넣어라

ex) cookie를 찾는 과정은 set_cookie(name, value, dys_to_expire)의 함수에 넣어라

ex) url을 만드는 과정은 make_url_friendly(business.name)으로 바꿔라

ex) Ajax 서버에 호출하고 응답처리를 위한 코드변경은 format_pretty(obj)으로 바꿔라

 

 

한번에 하나의 일만 수행하여라!

각 영역이 null or 특정 값을 가진다면, 해당 영역을 구분하고 특정 값을 위해 변수를 만들어본다.

//읽고자 하는 각각의 값을 위한 기본값을 정의한다.

string exit_state = "unknown";

string http_response = "unknown";

string content_type = "unknown";

 

프로그램을 평범한 말로 설명하고, 그 설명으로 더 자연스러운 코드를 작성한다.

"일치하지 않으면 앞으로 하나 더 나아가서 일치하게 한다" <- 이부분이 가장 지저분한 부분이며 새로운 함수로 분리한다.

분리 한 곳에서도 변수명을

 

*잘못된 예시

stock_time = stock_iter.time

price_time = price_iter.time

num_shares_time = num_shares_iter.time

 

*올바른 예시

t1 = row_iter1.time

t2 = row_iter2.time

t3 = row_iter3.time

과 같이 간단하게 할 수 있다. 

 

코드베이스를 최대한 작고 가볍게 유지하라

- 사용하지 않는 코드와 필요없는 기능 바로 제거

- 일반적인 '유틸리티'를 많이 생성하여 중복된 코드 제거하기

- 매일 15분씩 자신의 표준 라이브러리에 있는 함수/모듈/형들의 이름을 읽어라(C++ STL, 자바 API, 내장 파이썬 모듈..)

 

파이썬에서, [2,1,2] 같은 집합을 만들 때 set([2,1,2]), list(set[2,1,2]))을 사용한다.

 

테스트를 개선하기 위한 항목

-각 테스트의 최상위 수준은 최대한 간결해야 한다. 

-테스트에 실패하면 버그를 추적해서 수정하는 데 도움이 될 만한 에러 메세지를 출력해야 한다.

 

-코드의 구석구석을 철저하게 실행하는 가장 간단한 입력을 사용하라

CheckScoresBeforeAfter("2, 1, 3", "3, 2, 1"); //기본정렬

CheckScoresBeforeAfter("0, -0.1, -10", "0"); //0보다 작은 값을 모두 제거

CheckScoresBeforeAfter("1, -2, 1, -2", "1, 1"); // 중복은 문제되지 않는다.

 

-Test1()과 같은 이름 대신, Test_<함수이름>_<상황>과 같은 형태의 이름을 사용한다.

ex)Test_SortAndFilterDocs()

ex)Test_SoftAndFilterDocs_BasicSorting()

테스트를 위한 메소드명은 길어도 상관없다!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형