더스틴 보즈웰, 트레비 파우커 지음 / 한빛미디어

18.12.21-19.3.13

PART FOUR 선택된 주제들
[15]'분/시간 카운터'를 설계하고 구현하기

[14] 테스트와 가독성
테스트를 위한 코드는 쉽고 간결하게 써져야 하고 변경도 쉬워야 한다.

테스트시에 나오는 메시지도 신경 써서 만들어야 한다. 읽기 편하게. 그리고 원인을 쉽게 알 수 있게

테스트의 범위가 명확해야 한다.

하지만 테스트를 위해서 원래 코드가 나빠지면 곤란하다.

PART THREE 코드 재작성하기
[13]코드 분량 줄이기
가장 읽기 쉬운 코드는 아무 것도 없는 코드다
개발자가 짜고 싶은 코드나 기능? 하지마.
코드 베이스는 작게작게 - 기능을 줄이던, 라이브러리를 쓰던,
결국 다 테스트해야 한다

사용하지 않는 코드를 제거해라.

사용하는 라이브러리에 친숙해져라.
이거 관련된 뭔가가 있었는데 정도면 충분하다.


[12] 생각을 코드로 만들기
1 코드가 할 일을 옆의 동료에세 말하듯이 평범한 영어로 묘사해라
2 이 설명에 들어가는 핵심적인 단어와 문구를 포착하라
3 설명과 부합하는 코드를 작성하라

[11] 한번에 하나씩
한 번에 여러 가지 일을 수행하는 코드는 이해하기 어렵다.

*한번에 하나의 작업만 수행하게 코드를 구성해야 한다.
함수형 프로그래밍의 기본이기도 하다.


[10] 상관 없는 하위 문제 추출하기
1 주어진 함수나 코드 블록을 보고 스스로 질문하라 "상위 수준에서 본 이 코드의 목적은 무엇인가?"
2 코드의 모든 줄에 질문을 던져라 '이 코드는 직접적으로 목적을 위해서 존재하는가? 혹은 목적을 위해서 필요하긴 하지만 목적 자체와 직접적으로 상관 없는 하위 문제를 해결하는가?"
3 만약 상당히 원래의 목적과 직접적으로 관련되지 않은 하위 문제를 해결하는 코드 분량이 많으면, 이를 추출해서 별도의 함수로 만든다.

해당 함수, 클래스는 본인의 목적에 충실해야 한다. 하위 문제를 해결하는 코드가 많으면 추출해라.
너무 작은 단위로 추출하면 그게 또 나쁘다.

PART2 
루프와 논리를 단순화하기

[9] 변수와 가독성
변수를 엉터리로 사용하면 다음과 같은 문제가 발생한다
1 변수의 수가 많을 수록 기억하고 다루기 더 어려워진다
2 변수의 범위가 넓어질수록 기억하고 다루는 시간이 더 길어진다
3 변수값이 자주 바뀔수록 현재값을 기억하고 다루기가 더 어려워진다.

*변수 제거하기
불필요한 임시변수 제거하기
중간 값을 저장하는 변수 제거하기
흐름제어 변수 제거하기

*변수의 범위를 좁혀라
"전역 변수를 피하라"
범위가 넓으면 수정 및 사용영역을 가늠하기 힘들다.
- 변수가 적용되는 범위를 최대한 좁게 만들어라

- 많은 메소드를 정적 static으로 만들어서 클래스 멤버 접근을 제한하라 > 가급적 정적 메소드는 사람들에게 저 변수들로부터 독립적이라는 메시지를 줘야 한다 >>> 이것이 fn?
- 커다란 클래스를 여러 작은 클래스로 나누는 것도 방법이다.
변수 선언은 미리 앞단에 선언하는 것이 아니라
 그 때 그때 쓸 때 선언하는 것이 생각의 흐름에 적합한 편이다.

*값을 한번만 할당하는 변수를 선호하라
최대한 적게 변하는게 좋고 변하지 않아야 하는건 const 같은 키워드를 활용한다.



[8]거대한 표현을 잘게 쪼개기
*거대란 표현을 더 소화하기 쉬운 여러 조각으로 나눈다.

*거대한 구문 나누기

*표현을 단순화 하는 다른 창의적인 방법들
표현이 명확하고 버그를 만들 가능성이 적으면 매크로를 써서 가독성을 높인다.
하지만 이 말이 매크로를 권장한다는 것은 아니다.

-설명변수
if line.split(':').[0].strip() == "roor": 보다는

username = line.split(':').[0].strip()
if username == "root" 가 낫다.

-요약변수

and or 조건을 1줄에 축약하는 것보다 의미가 명확해진다면
2줄도 좋다는 생각을 가지자.

'영리하게' 작성된 코드에 유의하라, 나중에 다른 사람이 읽으면 그런 코드가 종종 혼란을 초래한다.

-복잡한 논리와 씨름하기
복잡한 논리가 중첩 되면 버그가 양산되니.
반대로 생각하는 것이 더 쉬울수 있다!


[7] 읽기 쉽게 흐름제어 만들기
*흐름을 제어하는 조건과 루프 그리고 여타 요소를 최대한 '자연스럽게 만들려고 노력해라
코드를 읽다가 다시 되돌아 가서 코드를 읽지 않아도 되게끔 만들어야 한다.

*if/else 블록의 순서
1-부정이 아닌 긍정 조건을 먼저
예. if(!debug) 보다 if(debug)
2-간단한 것을 먼저 처리하고
3-흥미롭고 확실한 것을 먼저
난 2번 보다 3번을 주로 선택해서 continue/ return이 늦게 나왓던 듯...팀장/소장에 비해서

3항 연산자는 가독성을 보고 가독성이 높아지는 조건에서만 쓰면 되겠다.
A ? B : C 인 경우
조건이 복잡하면 가독성이 떨어지나 조건이 간단하면 3항 연산자가 더 좋다.

*do/while loop를 피하라
조건이 뒤에 있고 한번을 실행해야 하고 continue를 쓰면 혼란스러우니 피하자
while을 쓰자

*함수 중간에서 반환하기
한 함수의 반환이 중간에 여러 번 있어도 전혀 문제가 없으니까 막 반환하자.

대신 클린업 보드를 위한 관용적인 구조를 이용한다
C++ : 소멸자
자바,파이썬 : try finally
파이썬 : with
c# : using

*악명 높은 goto
c를 제외하고 안 써야 한다
c도 줄여야 한다. 피할 수 있을 때까지 피해야 한다.

*중첩을 최소화하기
if / else 조건은 간단해야 좋고
조건의 중첩을 줄여야 한다.
담당 개발자에게 각 step 별로 중첩이 당연해보여도
그런 중첩이 일어난 상태에서 그 코드를 처음 보는 사람은 당황스럽다.
수정해야 하는 상황이라면 여러분의 코드를 새로운 관점에서 바라보라

함수 중간에서 반환하여 중첩을 줄여라.

루프내에 있는 중첩을 제거하기
<기존>
for(
   if(condition) {
      if() //
          if() //
   }//이게 내가 하덧 짓
<제안>

for(
 if(!condition)
    continue
    if()
       continue
         if()

*실행 흐름을 따라올 수 있는가?
쓰레딩 / 시그널,인터럽트 구조 / 예외 / 함수포인터&익명 함수 / 가상 메소드는
코드를 이해하기 어렵기 때문에 핵심 코드에서 지양하고 최소화한다

PART1 표면적인 수준에서의 개선

[6] 명확하고 간결한 주석 달기
*주석을 간결하게 하라
*모호한 대명사는 피해라
*

[5] 주석에 담아야 하는 대상
* 주석의 목적은 코드를 읽는 사람이 코드를 작성한 사람만큼 코드를 잘 이해하는데 돕는데 있다.

*설명하지 말아야 하는 것

코드에서 유추할 수 있는 것은 넣지 마라
예. //클래스 A를 위한 정의 //생성자 //이 값을 bool으로 반환 

나쁜 이름에 주석을 달지 말아라. 대신 이름을 고쳐라
좋은 이름의 함수가 좋은 주석보다 낫다

* 코딩을 수행하면서 머릿속에 있는 정보 기록하기

director's cut 같은 내용을 적어놔라

//이 주먹구구식 논리는 몇가지 단어를 생략할수 있다. 100% 해결은 어렵다
//이 클래스는 점점 엉망이 되고 있다. 어쩌면 하위 클래스를 만들어야 할지도 모름

그래야 최소 이 클래스의 상태를 보고 헛짓을 안 할 수 있다!

코드에 있는 결함을 설명하라
//todo : 더 빠른 알고리즘 필요
//todo(더스틴) : JPG말고 다른 포맷도 처리할 수 있어야 한다.
개선이 필요한 부분을 언급하는 것을 부끄러워 하지 마라 코드는 계속 진화해야 하니까.
흔히 쓰는 주석 키워드
todo : 할 것
fixme : 오작동이 알려진 코드
hack : 아름답지 않은 해결책
xxx: 위험해 여기

상수에 대한 설명은 가급적 하는 편이 낫다

* 코드를 읽는 사람의 입장에서 필요한 정보가 무엇인지 유추하기
나올 것 같은 질문에 대해선 주석 달기
코드를 작성할 때 "내가 작성한 이 코드를 다른 사람이 읽으면 깜짝 놀랄만한 부분이 있나?"를 생각해보자

큰 그림에 대한 주석~

복잡한 기능에 대한 요약 주석도 중요하다
라인별 함수별 주석이 있어도 무엇을 하는지에 대한 주석이 있어야 한다.

다음 단계로 주석을 단다
1 마음에 떠오르는 생각을 무조건 적어본다
2 주석을 읽고 무엇이 개선되어야 하는지 확인한다
3 개선한다.


[4] 미학
*일관성 있는 스타일은 '올바른'스타일이 더 중요하다.

*선언을 블록으로 나눠서 구분 짓게 하라

*의미 있는 순서를 선택하고 일관성 있게 사용하라
환자로 예로 들면
이름/성별/나이/주소 순으로 늘 비슷하게

*중복된 코드는 함수로 만들어 간결하게 만들어본다

*일관성과 간결성을 위해서 줄 바꿈을 재정렬하기
적절한 줄 바꿈과 들여쓰기는 가독성을 높인다.
일정한 글자수가 아니라.




좋은 소스는 '눈을 편하게'해야 한다.
- 코드를 읽은 사람이 이미 친숙한, 일관성 있는 레이아웃을 사용해라
- 비슷한 코드는 서로 비슷해보이게 하라
- 서로 연관된 코드는 하나의 블록으로 묶어라

요약
언제나 의미가 오해 되지 않는 이름이 최선의 이름이다.
의미에 대한 영어 단어의 한계가 있으니 max_, min_ 같은 접두어를 사용한다.

*이름을 짓기 위해서 복수의 후보를 평가한다.

* 사용자의 기대에 부응하기
getMean, list:size 등 반환이 바로 될거 같은 이름이지만,
아닌 경우도 있으므로 잘 파악해야 한다.
size란 이름은 O(1) 경우에만 붙여야 한다. 

* boolean 변수 이름 붙이기

예.bool read_password = true
의미가 모호하다.
우리는 패스워드를 읽을 필요가 있다 / 패스워드가 이미 읽혔다...
read같이 모호한 단어보다는
need_password | user_is_authenticated 같이 명시적인 단어를 사용하고

is, has, can, should 등의 단어를 이용한다.

부정적인 단어는 피한다.
bool disable_ssl = false;보단
bool use_ssl = true 가 더 읽기 좋다.


* 경계를 포함하는 범위에는 first / last를 써라.
시작은 포함하고 배열의 범위 밖까지 진행하는 것은 begin/end를 써라...이건 좀...지엽적인 듯한데.

* 경계를 포함하는 간게값을 다룰 때는 min / max를 이용해라.
한계를 설정하는 이름을 가장 명확하게 만드는 방법은 이름 앞에 max_, min_을 붙이는 것.
이상/초과, 이하/미만이 헷갈린다.
if shopping_cart.item_count > CART_TOO_BIG_LIMIT 보다는
if shopping_cart.item_count > MAX_ITEMS_IN_CART가 낫다~

[3] 오해할수 없는 이름들
본인이 지은 이름을 '다른 사람들이 다른 의미로 해석할수 있지 않을까? 라는 질문을 던져~

예. Filter
results = Database.all_objects.filter("year <= 2011")

문제점
대상을 고른다는 건지, 대상을 뺀다는 건지 알수가 없다
고른다면 select
빼는거라면 exclude가 더 낫다

예 Clip(text, length)
문장 끝에서 길이만큼 잘라낸다는 건지
문장을 처음부터 길이만큼 잘라내서 가진다는 건지 모호함

[2] 이름에 정보 담기

- 이름의 길이는?
좁은 범위에서는 짧은 이름이 괜찮다~
긴게 문제가 아니라 기억하기 어려운게 문제

흔한 약어는 괜찮다
evaluation > eval
document > doc
string > str

불필요한 단어는 제거하기

대문자/밑줄 등을 사용해라


-추상적인 이름보다 구체적인 이름을 선호하라.
변수명에 단위를 포함시키면 좋다.
밀리세컨드라면 _ms
초단위는 _secs
메가 바이트면 size_mb
다운로드 속도 단위는 max_kbps 등으로

-특정한 단어 오르기
size, get 으로 의미를 다 담기 어렵다
유의어 찾기, 동료에게 물어보기를 두려워하지 말라
- 재치 있는 이름보다 명확하고 간결한 이름이 더 좋다

tmp 같은 변수명은 전형적인 알고리즘 구현 내에서는 아주 좋지만
일반적인 기능에선 내용을 설명하지 못하니 안 좋다.
상황을 봐라

루프 반복자는 i,j, iter,it 보다 좋은 것을이 많다.
if(club[i].members[k] == users[j]) 보단
if(club[ci].members[mi] == user[ui]
ci 혹은 club_i, mi 혹은 members_i 같은 식이 버그 잡기 좋다
if(club[ci].members[ui] == user[mi] //index가 잘못된 상황임을 쉽게 알 수 있다.


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

내가 만든 코드도 6개월 지나면 어려워진다.
그러니 객관적으로 코드를 만들어야 한다.


by 무위자연 2019.03.13 16:55

QTableView의 ItemDelegate에서 해당 Row가 mouse hover 상태인지 직접 알수가 없다.


그래서 다음의 단계가 필요하다.


1. QTableView를 상속 받은 클래스를 만들고 해당 클래스는 다음과 같이 mousemove, mouse leaver에 대한 event를 처리하게 한다.

CustomTableView::CustomTableView(QWidget *parent) : QTableView(parent)

{

setMouseTracking(true);

}

void CustomTableView::mouseMoveEvent(QMouseEvent *event)

{

QModelIndex index = indexAt(event->pos());

emit hoverIndexChanged(index);

}

void CustomTableView::leaveEvent(QEvent *event)

{

emit leaveTableEvent();

viewport()->update();

}


2. 이 때 발생한 signal을 처리할 수 있도록 이 CustomTableView를 사용하는 곳에서 signal / slot을 설정한다.

connect(customTable_,

&ui::CustomTableView::hoverIndexChanged,

itemDelegate,

&ui::LoginInfoTableDelegate::slotHoverIndexChanged);

connect(loginInfoTable_,

&ui::CustomTableView::leaveTableEvent,

itemDelegate,

&ui::LoginInfoTableDelegate::slotLeaveTableEvent);


3. 사용할 ItemDelegate에서 다음과 같이 처리한다.

void CustomTableDelegate::slotHoverIndexChanged(const QModelIndex& item)

{

hoverRow_ = item.row();

}


void CustomTableDelegate::slotLeaveTableEvent()

{

hoverRow_ = -1;

}


4. 3번 slot으로 인해서 ItemDelegate의 Paint에서는 hoverRow_를 알 수 있게 된다.

void CustomTableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const

{

int column = index.column();

QStyleOptionViewItem itemOption(option);

bool isHovered = false;

if (index.row() == hoverRow_)

{

itemOption.state = itemOption.state | QStyle::State_MouseOver;

isHovered = true;

}

else

{

itemOption.state = itemOption.state &  (~QStyle::State_MouseOver);

}


이제 마음껏 hover 되었을 때의 효과를 반영하면 된다~



by 무위자연 2019.03.13 13:48
| 1 |