Transcript 이쁜코드작성하기
이쁜 코드
작성하기
2013.09. 13 마이에트 레이더즈팀 중원
다음 중 이쁜 얼굴은?
VS
다음 중 이쁜 코드는?
return exponent >= 0 ? mantissa * (1<<expnent) : mantissa / (1<< -expnent)
VS
If ( expnent >= 0)
{
return mantissa * ( 1 << exponent)
}
else
{
return mantissa / (1 << -exponent)
}
무엇이 코드를 이쁘게 만드는가?
할머니에게 설명할 수 없다면 당신은 제대로 이해한
것이 아니다.
- 알버트 아이슈타인
프로그램을 수행하는 일을 설명하는 가장 주된 방법은 소스 코드.
코드 역시 쉬운 말로 작성 해야..
이 함수를 보면 떠오르는 것은?
SendCharInfo(...)
SetItemID(...)
실제 작업이 이렇다면..
SendCharInfo(...)
{
로그인 정보 변경
캐릭터 맵핑
DB 갱신
...
블라블라..
캐릭터 정보 전송
}
SetItemID(..)
{
전 아이템을 모두 갱신
...블라블라
모든 유저에게 라우팅
}
뭔가 모호한 이름들..
treeSize(..)
=>트리의 노드 갯수 인가? 노드 깊이인가?
getPage(url)
인터넷에서 페이지를 가져오는 건가?
로컬에서 가져오는 건가?
bool read_password
패스워드를 읽을 필요가 있다는 건가?
패스워드가 읽었다는 건가?
이름에 정보를 담기
1. 가급적 보편적인 이름 피하기
Tmp, retval, foo, proc...
2. 추상적인 이름 대신 구체적인 이름.
ServerCanStart() => CanListenOnPort()
3. 이름에 추가적인 정보를 담기
Start(int delay) => Start(int delay_secs)
이름에 정보를 담기
4. 기대에 부흥하기
Get.. Set... , size() 가 하는 일, 걸리는 시간
적정한 이름의 길이
현재 긴 이름은 타이핑 상 문제가 되지 않는 시대
NewNavigationControllerWrappingViewControllerForDataSurceOfClass
NNClass
좁은 범위에서는 짧은 이름도 OK
if(debug)
{
map<string, int> m;
LookUpNamesNumbers(&m);
print(m);
}
debug용 m의 생명주기는 딱 3줄
하지만, m이 멤버거나 전역변수라면..?
LookUpNamesNumbers(&m);
print(m);
약어와 축약
doc => document
str => string
ToString => convertToString
아래는?
BFG => ???!
이름 길이에 대한 고찰
Code Complete에서 권하는 길이
일반적으로 10~16자, 8~20자 사이일 때, 디버깅이 용이했다.
사용범위가 넓다면 긴 이름을 사용하라
좁은 범위에 잠깐 사용하는 이름은 짧게 사용하라
코딩의 미학
Class StatsKeeper
{
Public:
/*일련의 더블변수값을
저장하는 클래스*/
Void Add(double d);
Private: int count;
double Average()
Private : double minimum;
list<double>
past_items; double max;
};
//일련의 더블 변수를 저장
//하는 클래스
Class StatsKeeper
{
Public:
void Add(double d);
double Average()
Private:
list<double> past_items;
int count;
double maximum;
double minimum;
};
일관성과 간결성을 위해 줄 바꿈을 재정렬 하자
public static final TcpConnectionSimulator wifi = new TcpConnetcionSimulator(500, /* kbps */80,/*흔들림*/200, /* 흔들림 */1 /* 패킷 손실 % */);
public static final TcpConnectionSimulator cell = new
TcpConnectionSimulator(
4500, /* kbps */
10, /* millisecs 대기시간*/
0, /* 흔들림 */
0 /* 패킷 손실 % */);
public static final TcpConnectionSimulator t3_fiber = new TcpConnectionSimulator(
4500, /* kbps */10, /* millisecs 대기시간*/
250, /* 흔들림 */ 0 /* 패킷 손실 % */);
public static final TcpConnectionSimulator wifi =
new TcpConnetcionSimulator(
500,
/* kbps */
80,
/*millisecs 대기시간*/
200,
/* 흔들림 */
1,
/* 패킷 손실 % */);
public static final TcpConnectionSimulator cell =
new TcpConnectionSimulator(
4500, /* kbps */
10, /* millisecs 대기시간*/
0, /* 흔들림 */
0 /* 패킷 손실 % */);
public static final TcpConnectionSimulator t3_fiber =
new TcpConnectionSimulator(
4500, /* kbps */
10, /* millisecs 대기시간*/
250, /* 흔들림 */
0 /* 패킷 손실 % */);
'비슷한 코드는 비슷하게 보여야 한다' 원리
public static final TcpConnectionSimulator wifi =
new TcpConnetcionSimulator(
500,
/* kbps */
80,
/*millisecs 대기시간*/
200,
/* 흔들림 */
1,
/* 패킷 손실 % */);
public static final TcpConnectionSimulator cell =
new TcpConnectionSimulator(
4500, /* kbps */
10, /* millisecs 대기시간*/
0,
/* 흔들림 */
0
/* 패킷 손실 % */);
public static final TcpConnectionSimulator t3_fiber =
new TcpConnectionSimulator(
4500, /* kbps */
10, /* millisecs 대기시간*/
250, /* 흔들림 */
0
/* 패킷 손실 % */);
// TcpConnectionSimulator
//
( 처리량,
[kbps]
지연속도, 흔들림,
[ms]
[ms]
패킷 손실)
[percent]
public static final TcpConnectionSimulator wifi =
new TcpConnetcionSimulator( 500,
80,
200,
1 );
public static final TcpConnectionSimulator cell =
new TcpConnectionSimulator( 4500,
10,
0,
0 );
250,
0 );
public static final TcpConnectionSimulator t3_fiber =
new TcpConnectionSimulator( 4500,
10,
'반복하지 말라(DRY)'는 원리
코드를 문단을 쪼갠다.
def suggest_new_friends(user, email_password):
Friends = user.friends()
friend_emails = set(f.email for f in friends)
contacts = import_contacts(uiser.email, email_password)
contact_emails = set(c.email for c incotacts)
non_friend_emails = contact_email – friend_emails
suggested_friends = User.objects.select(email_in=non_friend_emails)
display['user'] = user
display['friends']=friends
Display['suggested_friends'] = suggested_frieds
return render(“suggested_friedns.html”, display)
def suggest_new_friends(user, email_password):
#사용자 친구들의 이메일 주소를 읽는다.
Friends = user.friends()
friend_emails = set(f.email for f in friends)
#이 사용자의 이메일 계정으로부터 모든 이메일 주소를 읽어 들인다.
contacts = import_contacts(uiser.email, email_password)
contact_emails = set(c.email for c incotacts)
#아직 친구가 아닌 사용자들을 찾는다.
non_friend_emails = contact_email – friend_emails
suggested_friends = User.objects.select(email_in=non_friend_emails)
#사용자 리스트를 화면에 출력한다.
display['user'] = user
display['friends']=friends
Display['suggested_friends'] = suggested_frieds
return render(“suggested_friedns.html”, display)
문단으로 쪼갰을 때 장점
비슷한 생각을 하나로 묶어서 다른 생각과 구분된다.
문단은 시각적 디딤돌 역할을 한다.
하나의 문단에서 다른 문단으로 전진을 촉진 시킨다.
거대한 코드를 읽고 싶은 사람은
없다.
하지만 Extract Method 보다도,
가독성이 우선!!
이런 주석은 쓸모가 없다.
//클래스 ACount를 위한 정의
Class Account
{
public:
//생성자
Account();
//profit에 새 값 설정
void SetProfit(double profit);
//이 카운트에 profit을 반환
double GetProfit();
}
//해당 키를 위한 핸들을 놓아준다.
//이 함수는 실제 레지스트리를 삭제하지 않는다.
void DeleteRegistry(RegistryKey* key)
이런 주석은 필요한 것 같지만, 실은 이름을 잘못 지은 문제
=> void ReleaseRegistryHandle(RegistryKey *key)
주석따위!!!
난 소스로만 말한다!
왕년에 이런 아저씨들 많았다.
디카프리오는 정말 꿈에서 깨어났을까?
감독의 설명을 주석으로 만든다
// 이 데이터에서 이진트리는 해시테이블보다 40% 정도 빠름
// 해시를 계산하는 비용이 좌/우 비교를 능가함
=> 다른 사람이 불필요한 작업을 피할 수 있게 해준다.
NUM_THREADS = 8
//이 상수 값은 2 * num_processors
//값이다.
=> 상수 값이 결정된 사연을 알게 해준다.
//외부 서비스 호출하여 이메일 서비스를 호출한다.
//(1분 후 타임오버)
void SendEmail(string to, string subject, string body)
=> 다른 사람이 쉽게 빠질 것 같은 함정을 경고한다.
달지 말아야 할 주석
코드 자체에서 재빨리 도출될 수 있는 사실
나쁜 함수명과 같이 나쁘게 작성된 코드를 보정하려는 주석
필요한 주석
코드가 특정한 방식으로 작성된 이유를 설명해주는 내용
코드에 담긴 결함, 개선 방향 등
어떤 상수가 특정 값을 갖게 된 '사연'
주석을 잘 달려면...
읽는 사람 입장에서
=> 궁금해 할 내용을..
=> 쉽게 빠질 것 같은 함정을..
=> 큰 그림에 대한 주석을..
명확하게,
간단하게,
개괄적으로 설명
KISS (Keep It Simple! Stupid!) 원칙
흐름제어에 대한 가독성
If (a == b){
//첫번 째 경우
}else {
//두번 째 경우
}
If (a != b){
//두번 째 경우
}else {
//첫번 째 경우
}
어느 것이 가독성에서 유리할까?
흐름제어에 대한 가독성
If (a == b){
//첫번 째 경우
}else {
//두번 째 경우
}
If (a != b){
//두번 째 경우
}else {
//첫번 째 경우
}
-부정이 아닌 긍정을 다루는 것이 유리함.
-간단한 것을 먼저 처리하는 것이 유리함.
-더 흥미롭고, 확실한 것을 먼저 다루는 것이 유리함.
do {
continue;
} while (false)
이 구문은 어떻게 동작할까?
꺼려야할 do/while
do {
continue;
} while (false)
If, while, for문의 동작원리와 역순인 do/while 문은
코드를 2번 읽어야 함. 부자연스럽다.
내 경험으로 에러와 혼동의 원인은
do문에 있다.
그래서 나는 조건이 '눈에 뜨이는 곳에
미리' 나타나는 것을 선호한다.
인간 스택이 되어보자
if(isOpen()){
if(bSetup==true){
nCnt = MIN_SUM;
}
else{
if(nSum <5){
nCnt = 6;
}
else{
nCnt = 10;
}
}
}
else{
return nCnt = -1;
}
return nCnt;
스택의 해체
if(false ==isOpen())
return -1;
if(true==bSetup)
return MIN_SUM;
if(nSum <5)
return 6;
return 10;
일독 권합니다~
FIN