프로그래밍 수업 과제를 한다던까 할때

소스코드에 하이라이트를 주고싶다면 

https://carbon.now.sh


이 사이트를 써보자


예 ) 


뒷 배경과 소스코드 하이라이트 테마들은 변경 가능하다.


[ 읽기 쉽게 흐름제어 만들기 ] 

(1) 조건문에서 인수의 순서

if( 10 <= length ) 은 분명 아무문제 없지만

if( length >= 10 ) 이 더 읽기 쉽습니다.

왜 그럴까요.

영어 어순을 생각하시면됩니다. 

길이가 10이상이다가 , 10이상인게 길이다 보다 자연스러우니까요.

일반화 해보면

조건문에서 왼쪽은 값이 유동적인 " 질문을 받는 " 대상 이고 오른쪽고정적인 비교대상으로 사용되는 대상입니다.


[ 거대한 표현을 잘게 쪼개기 ]

드모르간의 법칙을 기억하시나요?

not ( a or b ) <=> (not a) and (not b) , not (a and b) <=> (not a) or (not b)

이 법칙을 이용하면 표현이 간단해질수 있습니다.


if(!(file_exists && !is_protected)) => if(!file_exists || is_protected) 어때요 깔끔해보이지 않아요?

not (file_exists and !is_protected)=> not file_exists = !file_exists or  not !is_protected = is_protected 이렇게 된것이 지요.


이렇게 4회에 걸친 책기반 강의를 마칩니다.


[미학]

- 선언문을 블록으로 구성하라

1
2
3
void add(n1, n2);
void saveData();
void del(n1, n2);

위 코드는 깔끔해보이지만 이게 몇십줄 되가면 복잡해집니다.

이럴땐 코드들을 블록으로 나누어 정리한다면 더 깔끔해집니다.

1
2
3
4
5
6
// calculate
void add(n1, n2);
void del(n1, n2);
 
// processing Data
void saveData();

이럴땐 코드들을 블록으로 나누어 정리한다면 더 깔끔해집니다.


[ 주석 ]

주석은 참 있으면 좋은데 그렇다고 너무 많으면 안좋고 애매한 녀석인데요. 최대한 이롭게 쓰는 방법을 알아봅시다.

- 설명하지 말아야 하는 것

(1) 함수명을 하나의 문장으로 정리한꼴 밖에 안되는 경우

1
2
3
4
5
6
7
// 주어진 이름과 깊이를 이용해서 서브트리에 있는 노드를 찾는다.
Node* FindNodeInSubtree(Node* subtree, string name, int depth);
 
// 주어진 'name'으로 노드를 찾거나 아니면 NULL을 반환한다.
// 만약 depth <=0 이면 'subtree'만 검색된다.
// 만약 depth == N 이면 N 레벨과 그 아래만 검색된다.
Node* FindNodeInSubtree(Node* subtree, string name, int depth);

위 코드의 첫번째 경우를 보면 그냥 함수를 알아차리기 쉬움에도 불구 하고 서술해 쓸데없는 주석일뿐입니다.

이런 코드에는 주석이 그리 필요해보이지 않으나 두번쨰 경우처럼 세부사항을 적어준다면 개발자들의 빠른 이해를 도와줄것입니다.


(2) 나쁜 이름을 주석으로 해결하려 드는 경우

1
2
3
4
// loadData()가 받아온 데이터를 암호화하는 함수
void enigma();
 
void EncodeData();

애니그마가 암호화랑 관련이 있다고는 하지만 이런식으로 작성하는것은 코드의 낭비입니다.

차라리 두번째 줄처럼 직관적으로 알 수 있게 해주는것이 훨 났습니다.


(3) 코드에 있는 결함을 설명하라 

개선이 필요하지만 못 고친것을 창피하게 여기지 마세요.

당당히 개선할점을 주석으로 달아주셔야 고쳐주기도 편하니까요.

몇가지 널리 사용되면 표시를 알려드리자면

 // TODO: -> 아직 하지 못해서 해야되는 일

// FIXME: -> 오작동을 일으키나 수정을 못하거나 안한 코드

// HACK: -> 해결을 하긴했는데 뭔가 불안하게 한 코드

// XXX: -> 위험! 여기 큰 문제가 있다!


(4) 상수를 설명해줘라

const int NUM_THREADS = 8;

이 상수는 별도의 설명이 필요없어 보이지만 

const int NUM_THREADS = 8; // 이 값이 2*num_processors보다 크거나 같으면 된다.

이렇게 설명이 있다면 상수값을 변경할때 더 이로울 것이다.


(5) 요약 주석 

긴 함수인데 이게 몇개의 큰 덩어리로 나누어질수 있다면 요약주석도 유용합니다.

1
2
3
4
5
6
7
8
9
10
void GenerateUserReport() {
  // 이 사용자를 위한 lock을 얻는다.
  ...
  // 데이터베이스에서 사용자의 정보를 읽는다.
  ...
  // 정보를 파일에 작성한다.
  ...
  // 사용자를 위한 lock을 되돌려 넣는다.
  ...
  }

처음 온 신입사원도 이런 주석들을 보면 코드를 쉽게 이해할 수 있겠죠?

본 강좌는 "읽기 좋은 코드가 좋은 코드다 - 더스틴 보즈웰, 트레버 파우커" 책을 100% 참고합니다.

[ 이름 오해를 피해라 ]

- Filter

필터는 거르는 대상을 코드를 보는 사람에 따라 오해할 가능성이 높습니다.

1
results = Database.all_objects.filter("year <= 2011");

위 코드를 보면 2011 이상을 고르는건지 제거하는 건지 궁금해집니다.

이런 이름은 안사용 하는게 최선입니다. 차라리 고르는것이라면 select(), 제거하는것이라면 exclude 가 낫습니다.


- 포함과 배제

1
print_range(start=2, stop=4);

이렇게 범위를 출력하는 함수가 있다고 해봅시다.

start 가 2인건 알겠는데 stop = 4 라.. 4를 출력후 멈추는지 4 출력전 멈추는지 알기가 힘듭니다.

경계를 포함하는 즉 2,3,4 인 상황일때는 first와 last를 사용하는것이 좋습니다.

또 포함, 배제가 동시에 일어날땐 어떻게 대처해야될까요?

그럴때는 begin/end를 사용하는 프로그래밍 관행이 있습니다.

end가 좀 애매하긴 하지만 대체제가 없으므로 그냥 쓰인다고 합니다.


- 불리언 변수에 정확한 이름 붙이기 

1
2
3
boolean read_password = true;
 
boolean disable_ssl = false;

위 두 예제는 다 좋지 않습니다.

일단 첫번째 코드는 read_password가 읽어야 되는게 true인건지 , 읽었다는게 true인건지 알기가 쉽지 않습니다.

이럴땐 need_password 나 user_is_authenticated 같이 의미를 정확히 알수있게 해주는것이 좋습니다.


또 두번째 코드는 이중부정을 사용하는 예인데요. 이중부정이란 말부터 그렇듯이 간결과 그렇게 어울리지 않습니다.

bool use_ssl = true 이렇게 부정을 사용하지 않으면서 간결하고 읽기 좋게 만들 수 있습니다.


- 프로그래머의 고정관념에 부흥하기 

1
public double getMean() {}

프로그래머들은 대개 get으로 시작하는 이름의 메소드는 가벼운 빠른 접근자로 내부의 변수를 반환한다고 생각합니다.

그렇기 때문에 이와같은 상황이 아닐때는 최대한 이러한 이름을 피해주는것이 좋습니다.

 

본 강좌는 "읽기 좋은 코드가 좋은 코드다 - 더스틴 보즈웰, 트레버 파우커" 책을 100% 참고합니다.

[ 이름에 정보 담기 ] 

- 구체적인 이름 사용하기

1
2
3
4
5
6
7
8
9
10
11
12
13
public void song() {
  int value = MusicPlayer.browser.showOpenDialog(null);
  if (value == JFileChooser.APPROVE_OPTION) {
    if (MusicPlayer.browser.getSelectedFile().isFile())
      song = MusicPlayer.browser.getSelectedFiles(); // file link
    else {
      filedir = MusicPlayer.browser.getSelectedFile();
      song = filedir.listFiles(new SongFilter());
    }
    fileSelected = true;
  } else
    fileSelected = false;
}

위 코드에서 public void song() 은 너무 설명이 적습니다. 

1
2
3
4
5
6
7
8
9
class LoadBtnAction implements ActionListener {
  public void actionPerformed(ActionEvent e) {
    Get get = new Get();
    get.song();
    Set set = new Set();
    set.song(get);
    MusicPlayer.songList_panel.refreshList();
  }
}

여기서는 코드를 읽는 사람에 따라 song이 가사를 반환할수도 아니면 노래 길이를 반환할수도 있습니다. 

그러므로 노래 파일을 받아오는 함수임을 한눈에 알도록 songFile로 고쳐주는것이 좋습니다.


- 더 '화려한' 단어 고르기

다양한 의미를 가진 단어대신 그 상황을 잘 보여주는 단어를 선택해주는것이 좋습니다.

예를 들면

start 를 launch, create, begin, open 등의 단어로 바꿔줌으로써 더 코드를 이해하기 쉽게 만들수 있습니다.


- 루프 반복자 

루프 반복시에 의미없이 사용하는 코드라해서 i , j , k를 남발하다 보면 잘못된 인덱스를 사용해도 알아차리기가 쉽지 않습니다.

1
if(clubs[ci].members[ui] == users[mi])

루프 반복자를 이렇게 변수에 맞게 써준다면 members 에서 ui 를 잘못사용하고 있다는것을 금방 알아차릴수 있겠죠?


- 단위를 포함하는 값들

ThrottleDownload(float limit) 이라는 코드가 있다 해봅시다. 

쓰로틀링의 제한이 어느정도인지 쉽게 알수있다면 코드의 문제점을 알기도 쉬울것입니다.

limit 대신 max_kbps 를 사용한다면 최대 몇 kbps일때 라고 쉽게 알수 있습니다.