좋은 이름에 대한 고려사항

변수의 이름에 따라 변수의 좋고 나쁨이 크게 좌우된다.

나쁜 변수명의 예제

> x = x - xx;

   xxx = fido + SalesTax(fido);

   x = x + LateFee(x1, x) + xxx;

   x = x + Interest(x1, x);

  //이 코드는 미결재 잔액과 새로 구입한 물건들을 토대로 고객이 지불해야 하는 총 금액을 계산하는 코드..라지만 각 변수의 의미를 알기 힘들다

좋게 수정하면

balance = balance - lastsPayment;

mothlyTotal = newPurchases + SalesTax(newPurchases);

balance = balance + LateFee (customerID, balance) + monthlyTotal;

balance = balance + Interest(customerID, balance);

 

명명(naming)시 가장 중요한 고려사항

변수이름이  변수가 표현하고 있는 것을 완벽하고 정확하게 설명해야 한다는 점이다. 종종 서술문 자체가 가장 좋은 변수 이름인 경우가 있다. 미국 올림픽대표팀에 있는 선수의 수를 표현하는 변수이름은 numberofPeopleOntheUsOlympicTeam일 것이다. 운동장 좌석수를 표현하는 이름은 numberofSeatsInTheStadium일 것이다. 현재의 이자율을 포함하는 변수는 r이나 x보다 rate나 interestRate가 더 좋은 이름이다.

이러한 이름에는 두가지 특징이 있음을 주목하자. 첫째, 판독하기 쉽다. 둘째는 몇몇 이름은 너무 길어서 실용적이지 않다.

이름은 가능한한 구체적이어야 한다. x, temp, i와 같이 한가지 이상의 목적으로 사용되기 쉬운 막연한 이름들은 많은 정보를 제공하지 않기때문에 나쁜이름이 된다.

 

최적의 이름 길이는 10~16자 사이가 적당하다고들 한다.

 

범위가 변수 명에 미치는 효과

전역공간(namespace)에 있는 이름들에는 한정자(qualifier)를 사용하라

 

변수이름에서의 계산값 한정자 - 많은 프로그램들은 계산된 값(총계, 평균, 최대값 등)을 보관하는 변수들을 갖는다. 만약 Total, Sum, Average, Max, Min, Record, String, Pointer와 같은 한정자로 변수의 이름을 수정한다면, 이름의 끝에 수정자를 입력한다. 이런 습관의 장점은 첫째, 변수 이름에서 가장 중요한 부분, 즉 변수의 의미를 가장 잘 전달하는 부분은 앞부분이기때문에 가장 눈에 띄고 가장 먼저 읽혀야 한다.  둘째, 이러한 규약을 마련함으로써 totalRevenue와 revenueTotal을 같은 프로그램에서 사용하게 되는 혼란을 피하게 된다. 셋째, revenueTotal, expenseTotal, revenueAverage와 같은 이름들은 정렬된 느낌을 주지 않는다. 마지막으로 일관성은 가독성을 향상시키고 유지보수를 쉽게 한다.

 

변수이름의 일반적인 반의어

  • begin/end
  • first / last
  • locked / unlocked
  • min / max
  • next / previous
  • old / new
  • opened / closed
  • visible / invisible
  • source / target
  • source / destination
  • up / down

 

데이터의 특정 타입에 대한 명명

루프인텍스의 명명

우리는 루프에서 i,j,k같은 이름을 관습적으로 쓴다.

ex 간단한 루프 변수 이름에 대한 자바 예제

for( i = firstItem; i < lastItem; i++)

data[ i ] = 0;

만약 변수가 루프 외부에서 사용되어야 한다면, 반드시 i나 j,k보다는 좀 더 의미 있는 이름을 제공해야 한다. 예를 들어, 파일로부터 레코드를 읽어들이고 얼마나 많은 레코드를 읽었는지 기억해야 한다면, recordCount와 같은 이름이 적절할 것이다.

ex 실명적인 루프변수 이름에 대한 자바 예제

recordCOunt = 0;

while( moreScores())

{

Score[ recordCount ] = GetNextScore();

recordCount ++;

}

//recordCount를 사용하는 코드

만약 루프가 길어진다면, i가 무엇을 나타내는지 잊기 쉽기때문에, 루프의 인덱스에 좀 더 의미 있는 이름을 제공하는 것이 좋을 것이다. 코드는 자주 변경되고 확장되고 다른 프로그램에 복사되기 때문에, 많은 숙련된 프로그래머는 i와 같은 이름들을 피한다. 루프가 길어지는 한가지 이유는 루프가 중첩되기 때문이다. 만약 여러개의 중첩된 루프가 있다면 가독성을 향상시키기 위해서 좀 더 긴 이름으로 루프 변수들을 작성한다

ex 중첩된 루프에서 좋은 루프 이름을 갖는 자바 예제

for ( teamIndex = 0; teamIndex < teamCount; teamIndex++)

{

for( eventIndex = 0; eventIndex < eventCount[ teamIndex ] ; eventIndex++)

{

score[teamIndex][eventIndex] = 0;

}

}

j,i보다는 teamIndex, eventIndex가 많은 정보를 제공한다

 

상태변수의 명명

상태변수에 대해서 flag보다 더 나은 이름을 생각한다.

ex 나쁜 이름을 갖는 플래그에 대한 예제

if ( flag ) ...

if( statusFlag & 0x0F) ...

if( printFlag == 16) ...

if(computeFlag == 0) ...

flag = 0x1;

statusFlag = 0x80;

printFlag = 16;

computeFlag = 0;

 

statusFlag = 0x80와 같은 명령문은 여러분이 코드를 작성하거나 관련문서를 보기전까지는 무엇을 의미하는지 알수 없다

ex상태변수를 잘 사용한 예제

if(dataReady) ...

if(characterType & PRINTABLE_CHAR) ...

if(reportType == ReportType_Annual) ...

if(recalcNeeded == True) ...

dataReady = true;

characterType = CONTROL_CHARACTER;

reportType = ReportType_Annual;

recalcNeeded = false;

이것이 좋은 명명 법이다.

 

임시 변수의 명명

임시변수는 계산의 중간 결과를 보관하기 위한 임시 저장소로 사용되고 보고 수잔으로 사용되는 값을 보관하기 위해 사용된다. 일반적으로 temp, x 또는 그 밖의 모호하고 설명적이지 않은 이름으로 명명된다.

임시변수를 조심해라. 변수의 값을 일시적으로 유지해야 할 필요가 종종있다.

 

ex 정보가 없는 임시 변수의 이름에 대한 예제

// 2차 방정식근을 구하는 코드이고 값은 양수를 가정한다

temp = sqrt(b^2 - 4 * a * c);

root[0] = (-b + temp) / ( 2 * a);

root[1] = ( -b - temp) / (2 * a);

temp가 어떤 값을 가지고 있는지 아무 정보가 없다.

 ex 임시 변수의 이름을 실질적인 변수로 대체한 예제

dicriminant = sqrt(b^2 - 4 * a * c);

root[0] = (-b + dicriminant ) / ( 2 * a);

root[1] = (-b - dicriminant ) / ( 2 * a);

 

boolean 변수의 명명

  • 전형적인 불린 변수의 이름을 기억한다. 다음은 유용하게 사용되는 불린 변수의 이름들이다

    • done - 무언가의 수행이 완료됨을 의미
    • error - 오류가 발생했음
    • found - 값이 발견되었음. 값이 발견되었으면 참 아니면 거짓.
    • soccess | ok - 연산의 성공을 의미.실패하면 거짓
  • 참이나 거짓의 의미를 함축하는 불린 변수의 이름을 사용한다. - 위와 같이 참 거짓이 명확한 명명은 좋지만 status / sourceFile같은 변수명은 참 거짓이 불분명하기때문에 좋지 않다. 어떤 프로그래머는 불린변수이름 앞에 Is를 붙이기 좋아한다. isdone? isError? isFound? 이 질문에 대한 참, 거짓의 대답을 제공한다. 하지만 isStatus?는 말이 안되는 이름인 것이다. 이 접근의 단점은 간단한 논리적인 표현식의 가독성이 떨어진다는 것이다. if(isFound)는 if(found)보다 가독성이 떨어진다.
  • 긍정적인 불린 변수이름을 사용한다 - notFound, notdone, notSuccessful보다는 found/done/processingComplet가 훨씬 좋다.

 

열거 형의 명명

열거형을 사용할때, Color_, Planet_, Month_와 같은 접두사를 사용하여 해당타입의 멤버들이 모두 동일한 그룹에 속한다는 것이 분명하다는 것을 보장할수 있다.

 

상수의 명명

상수를 명명할때 상수가 가르키는 숫자보다는 상수가 표현하는 추상적인 대상으로 명명하도록 한다. FIVE는 나쁜 이름이다. CYCLES_NEEDED는 좋은 이름이다.FIVER = 6.0도 웃긴 이름이다. 마찬가지로 BAKERS_DOZEN은 잘못된 상수이름이며,DONUTS_MAX는 좋은 이름이다.

 

명명 규약의 효과

규약의 장점

  • 더 많은 것을 당연하게 받아들일수 있다
  • 다른 프로젝트에서 활용할수 있다. 유사한 이름은 무엇을 해야 하는지 익숙하지 않은 프로젝트에서 도움을 준다
  • 새로운 프로젝트에서 좀 더 빠르게 코드를 배우는데 도움을 준다. 프로그래머마다 다르게 보일수 있는 코드를 일관성있게 만들어 준다
  • 이름이 늘어나는 것을 줄여준다. 예를 들어 총점을 pointTotal이나 totalPoints로 부를수 있다. 이것은 코드 작성시에는 괜찮지만 가독시 혼란스러울수 있다
  • 언어의 약점을 보완할수 있다
  • 관련된 항목들간의 관계를 강조한다. 객체데이터를 지원하는 언어를 사용한다면 컴파일러가 자동으로 처리하지만 그렇지 않을 경우 명명 규약으로 보완할수 있다. address, phone, name과 같은 이름들의 변수가 있고 직원(employee)와 관련된 변수들이라면 employeeAddress, employeePhone, employeeName 이라 지어보자. 관련성이 눈이 보일 것이다.

 

언제 명명 규약이 필요한가? - 솔직히 항상 필요한 거 같기는 하다.

  1. 한 프로젝트에서 여러 명의 프로그래머가 작업할때
  2. 수정이나 유지보수때문에 프로그램을 다른 프로그래머에게 넘겨주어야 할때(거의 항상)
  3. 조직 내에 있는 다른 프로그래머가 여러분이 작성한 프로그램을 검토할때
  4. 프로그램이 너무 커서 한번에 기억할수 없기때문에 반드시 부분적으로 제쳐두어야 할때
  5. 프로젝트에서 자주 쓰이는 특이한 용어들이 많아서 코드 작성시 사용할 표준 용어나 약어가 필요할때

 

비형식적인 명명 규약

언어에 독립적인 규약을 위한 지침

  • 변수 이름과 루틴 이름을 구별한다 - 변수이름은 소문자로 시작하고 루틴이름은 대문자로 시작하는 경우가 많다
  • 클래스와 객체를 구별한다

    1. 첫번째 문자를 대문자로 작성하여 타입과 변수를 구분함

      ex. LongerWidget longerWidget;

    2. 모든 문자를 대문자로 작성하여 타입과 변수를 구분함

      ex. WIDGET widget; LONGERWIDGET longerWidget;

    3. 타입에 대해서 "t_" 접두사를 작성하여 타입과 변수를 구분함

      ex. t_Widget Widget; t_LongerWidget LongerWidget;

    4. 변수에 대해서 "a"접두사를 작성하여 타입과 변수를 구분함

      ex. Wdiget aWidget; LongerWdiget aLongerWidget;

    5. 보다 구체적인 변수의 이름을 사용하여 타입과 변수를 구분함

      ex. Widget employeeWidget; LongerWidget fullEmployeeWidget;

      1~5는 각각 tradeoff가 있다. 각자 사용하는 언어의 특성에 맞추어 알아서 사용하면 된다. 예를 들어 1번째 경우 VB에서는 widget과 Widget을 같은 토큰으로 간주해서 사용할수 없다.

전역 변수를 식별한다 - 예를 들어 "g_"같은 접두어를 붙여서 식별한다

멤버변수를 식별한다 - 예를 들어 "m_"같은 접두어

형 선언을 식별한다 - 접두어 "t_"

명명된 상수를 식별한다

열거형의 요소를 식별한다

입력만 하는 매개변수를 지정할수 없는 언어에서는 이를 식별한다

가독성을 강화시키기 위해서 이름을 형식화한다- 가독성을 높이기 위해서 대문자를 사용하는 방법과 공백문자를 사용하는 방법이 있다. GYMNASTICSPOINTTOTAL은 gymnasticsPoi0ntTotal이나 gymastics_point_total보다 가독성이 떨어진다. 이런 기법은 섞어서 사용하지는 말자.

 

특정언어에 대한 지침. - 너무 구체적임으로 책 참고 - p395~p397.게다가 현재 난 AS를 사용한다

 

표준화된 접두사들

표준화된 접두사는 사용자 정의 타입의 축약형(User-Defined Type, UDT)와 의미적인 접두사로 나눈다.

UDT - 특정 프로그램을 위해서 작성한 짧은 코드로 기술한 후, 그 프로그램에서 사용하기 위하여 표준화한다

아래 표는 워드 프로세서를 위한 프로그램에 사용할수 있는 간단한 UDT목록이다.

UDT 축약형 의미
ch 문자에서(c++에서의 문자가 아니라 워드 프로세서 프로그램 문서에 있는 문자를 표현하기 위한 데이터형
doc 문서.document
pa paragraph
scr 화면 영역(screen region)
sel 선택(selection)
wn 윈도우(window)

의미적 접두사 - 의미적 접두사는 UDT보다 한 단계 더 나아가서 변수나 객체가 어떻게 사용되는지를 설명한다

의미적 접두사 의미
c 카운트(count), 레코드나 문자 등의 수에서 사용된다
first 배열에서 다루어져야 하는 첫번째(first)요소이다. first는 min과 유사하지만 배열 자체보다는 현재의 연산과 관계가 있다
g 전역변수
i 배열에 대한 인덱스
last 배열에서 다루어져야 하는 마지막(last)요소이다. last는 first의 반대
lim 배열에서 다루어져야 하는 요소의 상한ㄱ밧
m 클래스 수준의 변수
max 배열이나 다른 종류의 리스트에서 절대적인 마지막 요소. max는 배열에 대한 연산보다는 배열자체를 가르킨다
min 배열이나 다른 종류의 리스트에서 절대적인 첫번째 요소
p 포인터

표준화된 접두사의 장점

모호해지기 쉬운 명명 영역을 정교하게 만든다. min과 first, last, max간의 정확한 구분은 특히 도움이 된다.

간결한 이름을 만든다. 예를 들어 단락수를 나타내기 위해 totalParagraphs 대신 단락수로 cpa를 사용할수 있고 indexParagraphs나 paragraphsIndex대신에 단락배열에 대한 인덱스를 구별하기 위해서 ipa를 사용할수 있다.

표준화된 접두사를 이용하면 컴파일러가 검사할수 없는 추상 데이터 형을 사용하고 있을 때 타입을 정확하게 검사할수 있다. paReformat = docReformat은 pa와 doc가 서로 다른 UDT이기때문에 아마 틀렸을 것이다.

표준화된 접두사의 가장 큰 위험은 프로그래머가 접두사에 이어서 변수에 의미 있는 이름을 제공하려고 하지 않는 다는 점이다.

 

읽기 쉬운 짧은 이름

일반적인 축약 지침

  • 표준 축약어를 사용한다(사전에 나와 있는 널리 사용되는 용어)
  • 불필요한 모음을 제거한다(ex. computer > cmptr, screen > scrn, apple > appl, integer > intgr) -> 한국인이라 그런지 공감 안됨. 더 모르겠음
  • 관사(and, or, the )를 제거한다
  • 각 단어의 첫번째 문자나 처음의 몇 문자를 사용한다
  • ...

축약어에 대한 의견.

-->별공감은 가지 않지만 한가지는 기억해둘만 하다. 주요 축약어에 대한 설명을 주석으로 넣어둔다.

ex. xPos x값의 위치 . 이런 식으로.

 

피해야 할 종류의 이름

  • 오해의 소지가 있는 이름이나 축약어를 피한다
  • 유사한 의미를 갖는 이름을 피한다
  • 의미는 다르지만 유사한 이름의 변수를 피한다
  • "개"와 "게"처럼 비슷하게 들리는 이름을 피한다 > 동의이의어
  • 이름에서 숫자를 피한다 - 예를 들어 file1과 file2 또는 total1과 total2는 피한다.
  • 이름에 철자가 틀린 경우가 없도록 한다
  • 일반적으로 틀리기 쉬운 단어는 피하도록 한다- 철자가 미국인도 헷갈리는 것들이 참 많구나
  • 대문자 만드로 변수의 이름을 차별화하지 않는다.
  • 여러개 언어의 사용을 피한다
  • 표준형, 변수, 루틴의 이름을 피한다 - 일밙적으로 예약어.
  • 변수가 표현하는 것과 전혀 관련이 없는 이름을 사용하지 않는다
  • 읽기 어려운 문자를 포함하는 이름을 피한다

 

 

요점정리

  • 좋은 변수 이름은 프로그램의 가독성에 있어서 핵심적인 요소이다
  • 이름은 가능한한 구체적이어야 한다
  • 명명규약은 지역, 클래스, 전역 데이터를 구분한다. 그리고 형이름, 명명된 상수, 열거형, 변수를 구분한다
  • 축약어는 현대적인 프로그래밍 언어에서는 거의 필요하지 않다.
  • 코드는 작성되는 것보다 훨씬 많이 읽혀진다. 여러분이 선택한 이름이 작성 시간 편의성보다 읽기 시간 편의성에 유리하도록 한다

이 글은 스프링노트에서 작성되었습니다.

by 무위자연 2008. 1. 19. 16:58

일반적인 숫자

매직넘버는 피한다 : 아무 의미 없는 혹은 의미를 알기 힘든 숫자를 사용하는 것을 지양한다

ex for i= 0 to 99 do... 이것보다는 for i = 0 to MAX_ENTRIES - 1 do... 이 코드의 가독성도 높이고 숫자가 의미하는 바가 확실하다.

하지만 필요하다면 0과 1은 그냥 사용한다

0으로 나눔오류를 미연에 방지한다.

형변환을 명확하게 수행한다. -> 잘못된 형변환은 값의 손실을 가져온다.

서로 다른 형을 비교하지 않는다. -> 이것 역시 잘못된 결과값이나 값의 손실을 가져올수 있다.

컴파일러의 경고에 주의를 기울여라

 

정수

정수나눗셈을 검사한다 - 7/10 의 결과는 반드시 0.7이 아닐수 있고 언어마다 다를수 있다. 가장 좋은 방법은 (10 * 7) / 10으로 바꾸는 것이다.

정수 오버플로우를 검사한다

중간 결과에 오버플로우를 검사한다

 

부동소수점

부동소수점에서 가장 중요하게 고려해야 할 사항은 많은 분수값이 디지털 컴퓨터의 0과 1을 사용하여 정확하게 표현될수 없다는 점이다.

서로 크기가 매우 다른 수를 더하거나 빼지 않는다

동치비교를 피한다

라운딩 오류를 예측한다 - 더 큰 정밀도를 갖는 변수 형으로 변경한다, 이진화 십진 표기법 변수로 변경한다, 부동소수점을 정수형 변수로 변경한다

특정 데이터형을 지원하는 언어와 라이브러리가 있는지 확인한다

 

문자와 문자열

매직 문자와 문자열을 사용하지 않는다

하나 모자름(off-by-one)오류를 주의한다 - 서브 문자열은 배열처럼 인데스로 나타낼수 있기때문에, 문자열의 범위를 지나서 읽거나 쓰는 하나 모자름 오류를 주의한다

사용하고 있는 언어의 환경에서 유니코드를 어떻게 지원하는지 알아보도록 한다

국제화/ 지역화 전략을 초기에 결정한다 - 나한테 다국어버전 이슈로 다가옴!!!!

문자열 타입간에 일관된 변환 전략을 결정한다

 

C에서의 문자열

문자열 포인터와 문자 배열간의 차이점을 이해하도록 한다

 C방식의 문자열을 CONSTANT+1의 길이를 갖도록 선언한다

ex. char name[NAME_LENGTH + 1] = {0}; // 길이가 NAME_LENGTH인 문자열

끝이 없는 문자열을 피하기 위해서 문자열을 널(null)로 초기화한다

ex. char EventName[MAX_NAME_LENGTH + 1] =  {0};

C언어에서 포인터 대신 문자 배열을 사용한다 - 메모리 제약만 없다면 모든 문자열 변수들을 문자 배열로 선언한다

끝이 없는 문자열을 피하기 위해서 strcpy()대신 strncpy()를 사용한다

 

 Boolean 변수

프로그램을 문서화하기 위해서 불린 변수들을 사용한다. - 불린 표현식을 테스트 하는 대신, 표현식의 의미가 보다 명확하도록 변수에 할당한후 테스트를 수행한다

ex. 불린 테스트의 목적이 명확하지 않은 자바 예제

if((elementIndex < 0) || ( MAX_ELEMENTS < elementIndex) || (elementIndex == lastElementIndex)){ ... }

ex. 불린 테스트의 목적이 명확한 자바 예제

finished = (elementIndex < 0) || ( MAX_ELEMENTS < elementIndex);

repeatedEntry = (elementIndex == lastElementIndex);

if( finished || repeatedEntry) { ... }

복잡한 테스트를 단순하게 하기 위하여 불린 변수를 사용한다 - 여러개의 조건을 나누어 불린 변수에 넣고 그 변수로 if문의 조건을 만든다. 위의 예처럼.

필요하다면, 고유한 불린 타입을 만든다.

 

열거형

가독성을 향상시키기 위해서 열거형을 사용하라

신뢰성을 위해서 열거형을 사용한다

수정 용이성을 위해서 열거형을 사용한다

불린 변수에 대한 대안으로 열거형을 사용한다.

타장하지 않은 값을 검사하라 - if나 case문에서 열거형을 사용할때, 타당하지 않은 값을 검사한다. 타장하지 않은 값을 잡기 위해서 case문에서는 else문을 사용한다

ex. 열거형에서 타당하지 않은 값을 검사하는 풀륭한 VB예제

Select Case screenColor

case Color_Red //...

case Color_Blue //...

case Color_Green //...

case Else -> 여기에서 타장하지 않은 값을 테스트하고 있다.

        displayInternalError()

반복문의 범위를 지정하기 위해서 열거의 처음과 마지막 엔트리를 정의한다

열거형의 첫번째 항목을 타당하지 않은 값으로 남겨둔다 - 많은 컴파일러들은 열거 형의 첫번째 요소를 0으로 할당한다. 0으로 매핑된 해당요소를 타당하지 않은 값으로 선언하면 초기화되지 않은 변수들은 0일 확률이 높기때문에, 그러한 변수들을 잡는데 도움을 준다.

프로젝트 코드작성표준에서 처음과 마지막 요소가 어떻게 사용될 것인지를 정확하게 정의한 후, 일관성 있게 사용한다

열거의 요소에 명시적인 값을 할당할때 발생할수 있는 위험요소들을 주의한다

 

만약 언어가 열거형을 지원하지 않는 다면? 전역변수나 클래스로 열거형을 흉내낼수 있다.

ex 열거형을 흉내내는 자바예제

class Country

{

private Country()

{

public static final Country China = new Country();

public static final Country England = new Country();

public static final Country Korea = new Country();

//..

}

}

 

명명된 상수

데이터선언에서 명명된 상수를 사용해라 - 가독성과 유지보수에 도움을 준다

"확실한" 리터럴이라도 리터럴은 피한다.

적절하게 지역변수나 클래스를 사용하여 명명된 상수를 흉내낸다

명명된 상수를 일관성 있게 사용해라

 

배열

배열은 모든 항목이 동일한 형이고 배열 인덱스를 사용하여 직접적으로 접근 가능한 항목을 포함한다

배열의 모든 인덱스가 배열의 경계내에 있는지 확인한다

배열 대신 컨테이너의 사용을 고려하거나 배열을 순차적인 구조체로 생각하라

배열의 마지막 위치를 확인해라

다차원 배열에서는 첨자(subscript)가 정확한 순서대로 사용되고 있는지를 확인하도록 한다

인덱스가 혼선(cross-talk)되지 않도록 주의한다 - 중첩된 루프경우 Array(i)를 Array(j)이라고 작성하기 쉽다. 조심하자.

C에서는 배열을 다루기 위해서 ARRAY_LENGTH()매크로를 사용한다.

ex #defineARRAY_LENGTH (x) (sizeof(x) / sizeof(x[0]))

 

새로운 형 만들기

새로운형을 만들기 위한 지침들

기능 지향적인 이름으로 형을 생성한다 - 새로운 형이 표현하고 있는 실제 세계문제의 일부분을 가르키는 형이름을 사용한다.

미리 정의된 형을 피한다

미리 정의된 형을 재정의하지 않는다 - 표준형의 선언을 변경하면 혼란스럽다

이식성을 위해서 대체형을 정의하라

tyepdef를 사용하는 대신 클래스 생성을 고려해본다.`

이 글은 스프링노트에서 작성되었습니다.

by 무위자연 2008. 1. 19. 16:58

구조체

서로 다른 형들의 집합으로 구성된 데이터를 의미한다. 배열은 이 중 특이한 경우인 것이다. 보통은 구조체보다는 클래스를 선호한다

구조체를 사용하는 예

데이터 관계를 이해하기 쉽게 하기 위해서 구조체를 사용한다

ex 오해의 소지가 있고 구조화되지 않은 변수를 사용한 VB 예제

name = inputname;

address = inputAddress;

phone = inputphone;

title = inputtile ;//...

ex 보다 많은 정보를 제공하고 구조화된 변수를 사용하는 VB 에제

employee.name = inputname;

employee.address = inputAddress;

employee.phone = inputphone;

employee.title = inputtile; //...

 

데이터 블록에 대한 작업은 단순화하기 위해서 구조체를 사용해라

매개변수 목록을 단순화시키기 위해서 구조체를 사용해라 - 구조체를 사용하여 루틴의 매개변수를 단순화할수 있다. 필요한 각각의 요소들을 개별적으로 전달하는 대신 연관된 요소들을 구조체로 묶은 다음, 구조체로 전체 내용을 전

달할수 있다.

ex 구조체를 사용하지 않고 서투른 방법으로 루틴을 호출하는 VB예제

HardWayRoutine { name, address, phone, ssn, gender, salary }

ex 구조체를 사용하여 세련된 방법으로 루틴을 호출하는 VB 예제

EasyWayRoutone { employee }

만약 numWithholdings를 추가하고 싶다면 HardWayRoutine 에 대한 호출을 모두 변경해주어야 한다. 하지만 employee 에 추가한다면 EasyWayRoutone 는 매개변수를 신경쓸 필요가 없다

유지보수를 줄이기 위해서 구조체를 사용해라 - 구조체를 사용하면 연관된 데이터를 그룹으로 묶기때문에 구조체를 변경할때 프로그램을 변경하는 경우가 적다.

 

포인터

포인터 사용은 현대적인 프로그래밍에서 오류를 유발할 가능성이 가장 높은 분야 중 하나이다. 그리고 자바와 C#, VB와 같은 현대적인 어너는 포인터 데이터형을 제공하지 않는다. 하지만 포인터를 잘 이해하면 프로그래밍 언어의

동작방식을 이해하는데 도움이 되고 여러가지 방어적인 프로그래밍 방법들에도 많은 도움이 된다.

포인터를 이해하는 패러다임

메모리상에서의 위치 - 메모리상에서의 위치는 주소이며 종종 16진수 표기법으로 표현된다. 포인터 자체는 이 주소만 포함한다. 포인터가 가르키고 있는 데이터를 사용하기 위해서는 해당주소로 가서 메모리의 내용을 해석해야 한다

메모리의 내용을 해석하는 방법 - 해당 포인터의 기본형에 의해서 제공된다. 정수를 가르키면 정수로, 문자열을 가르키면 문자열로 해석할수 있다.

포인터 사용의 팁

포인터 오류는 일반적으로 포인터가 절대로 가리켜서는 안되는 어딘가를 가르키기때문에 발생한다. 잘못된 어떤 값을 포인터 변수에 할당할때 써서는 안되는 메모리 영역에 데이터를 쓰게 된다. 이를 메모리 충돌(memory corruption) 이라 한다. 포인터의 성공적인 사용을 위해서 첫째, 처음부터 포인터 오류를 만들지 않는다. 둘재, 포인터 오류가 코드에 작성된 후 가능한한 빨리 발견되도록 한다.

  • 포인터 연산을 루틴이나 클래스로 고립시킨다.
  • 포인터를 선언과 동시에 정의해라.

        ex 잘못된 포인터 초기화 C++예

        Employee *employeePt;

        // 수 많은 코드

        employeePtr = new Employee();

       ex 포인터를 훌륭하게 초기화한 C++예

       //수 많은 코드

      Employee *employeePtr = new Employee();

  • 포인터를 할당된 곳과 같은 영역 내에서 삭제한다
  • 포인터를 사용하기 전에 검사한다
  • 포인터가 참조하는 변수를 사용하기 전에 검사한다
  • 손상된 메모리를 검사하기 위해서 도그태그(dog-tag)필드를 사용한다.
  • 명시적으로 중복하여 추가한다
  • 여분의 포인터 변수들을 사용한다ㅏ - 절대로 포인터 변수들을 아끼지 않는다. 변수는 하나 이상의 목적으로 사용되어서는 안된다.
  • 복잡한 포인터 표현식을 단순화해라
  • 그림을 그린다. - 포인터를 코드로 설명하면 혼란스러울수 있다. 일반적으로 그림을 그리는 것이 도움이 된다.
  • 연결 리스트에 있는 포인터들을 올바른 순서로 삭제한다
  • 임시메모리를 할당한다 - 만약 프로그램이 동적 메모리를 사용항다면, 갑자기 메모리 부족하게 되어 RAM공간에 있는 데이터를 잃게 되는 문제를 피해야 한다. 이러한 문제를 피하는 방법중에 하나가 임시메모리를 할당하는 것이다. 작업을 저장하기 위해서 얼마나 많은 메모리가 필요한지 결정한 다음, 작업을 정리한후 우아하게 프로그램을 종료시킨다. 즉 프로그램이 시작할때 메모리를 할당하고 남겨둔다. 그러면 프로그램에서 메모리가 부족하게 될 경우 임시 메모리를 해제한후 작업을 정리하고 종료한다
  • 쓰레기를 확실하게 제거해라.
  • 메모리를 삭제하거나 해제한 다음 포인터를 null로 설정한다.
  • 변수를 삭제하기 전에 잘못된 포인터를 검사한다
  • 포인터할당을 추적한다
  • 포인터문제를 피하기 위한 전략에 집중하기 위하여 커버(cover)루틴을 작성한다

 

C++ 포인터 포인터

  • 포인터와 참조의 차이점을 이해하라 - 포인터(*)와 참조(&) 모두 객체를 간접적으로 참조한다. 초보자입장에선 object->field와 object.field로 참조하는 외형 상의 차이가 있다. 하지만 가장 중요한 차이점은 참조가 항상 객체를 참조하는 반면에 퐁린터는 널을 가르킬수 있고 참조는 참조하는 대상이 초기화 된 후로 변경될수 없다
  • 참조로 전달 매개변수에 포인터를 사용하고 값으로 전달매개변수에 const참조를 사용해라 - 수정가능한 객체에서는 멤버에 대한 참조를  object->member표기법으로 사용할 것이며, 수정 가능하지 않은 객체에는 멤버에 대한 참조를 object.member표기법으로 사용할 것이다.
  • auto_ptrs를 사용해라 - auto_ptrs를 사용하는 습관을 들이도록 하자. auto_ptrs이 범위를 벗어날때 자동으로 메모리를 삭제함으로써, 일반적인 포인터와 관련된 많은 메모리 누수문제를 피할수 있다.
  • 스마트포인터(smart pointer)사용해라. - 스마트 포인터는 일반 포인터와 비슷하게 작동하지만 자원관리, 복사작업, 할당 연산, 객체 생성과 소멸등의 기능을 제공한다.

 

C - 포인터 포인터

  • 기본 형 대신 명시적인 포인터 형을 사용해라 ex NodePtr = (NODE_PTR) calloc (1, sizewof(NODE));
  • 형 변환을 피해라
  • 매개변수를 전달하고자 할때에는 별표규칙을 따른다.
  • 메모리 할당시 변수의 크기를 결정하기 위해서 sizeof()를 사용해라

 

전역데이터 -  어디서나 접근 가능한 데이터라 편리할수 있다. 그러나 숙달된 사람은 지양한다

전역 데이터를 사용할때 발생하는 일반적인 문제점

  • 전역 변수에 대한 부주의한 변경
  • 전역 데이터의 기괴하고 이상한 별칭 문제들
  • 전역 데이터의 재진입코드 문제 - 다중 thread일때 심각한 문제를 야기할수 있다.
  • 전역 데이터로 인한 코드 재사용문제 - 전역 데이터 변경은 원본 프로그램에 영향을 미칠뿐 아니라 오래된 클래스를 사용하는 모든 새로운 프로그램들에게도 영향을 미친다.
  • 전역 데이터와 관련된 불확실한 초기화 순서 문제점 - 서로 다른 번역 유닛사이에서 데이터가 초기화되는 순서가 어떤 언어 특히 C++에서는 정의되어 있지 않다.
  • 전역데이터로 인해 모듈화와 지적인 관리 용이서으이 손상

 

전역데이터를 사용하는 이유

  • 전역적인 값을 보관한다
  • 명명된 상수를 흉내내라
  • 열거형을 흉내내라
  • 매우 자주 사용되는 데이터에 대한 사용을 능률적으로 처리하라
  • 뜨내기 데이터(tramp data)를제거한다

최후의 수단으로만 전역데이터를 사용해라

  • 각 변수들을 로컬로 만든 다음, 필요할 때에만 변수를 전역으로 만든다 -  처음에는 지역변수로 만들고 필요할때 private와 protected로 전역을 선언한다
  • 전역 변수와 클래스 변수를 구분하라
  • 접근 루틴을 사용해라

 

전역데이터 대신 접근 루틴의 사용

전역데이터로 할수 있는 것은 접근 루틴으로 더 잘할수 있다. 접근 루틴의 사용은 추상데이터 형을 구현하고 정보 은닉을 이루기 위한 핵심기술이다.

접근 루틴의 장점

  • 데이터에 대한 제어를 집중시킬수 있다.
  • 변수에 대한 모든 참조가 보호되고 있음을 보증할수 있다
  • 자동으로 정보 은식이 갖는 일반적인 이점들을 얻게 된다.
  • 접근 루틴은 추상데이터형으로 변환하기 쉽다.

접근 루틴의 사용방법

  • 모든 코드가 접근 루틴을 통해서만 데이터에 접근할수 있도록 한다
  • 모든 전역 데이터를 한곳에 집어 넣지 않는다
  • 전역 변수에 대한 접근을 제어하기 위해서 잠금(locking)을 사용해라
  • 추상화 수준을 접근 루틴에 만든다
  • 데이터에 대한 모든 접근을 동일한 추상화 수준에서 유지한다

 

전역 데이터를 사용할때의 위험요소를 줄이는 방법

전역 변수를 분명히 하는 이름 규약을 만든다

모든 전역 변수에 주석을 잘 작성한 목록을 만든다

중간 결과를 포함하기 위해서 전역변수를 사용하지 않는다

전역데이터를 사용하지 않는 것처럼 가장하기 위해 모든 데이터를 거대한 객체에 입력하고 모든 것에 전달하는 방법을 사용하지 않는다

 

요점 정리

  1. 구조체는 프로그램을 덜 복잡하게 하고 이해하기 쉽게 만들며 유지 보수 하기 쉽게 만드는데 도움을 줄수 있다
  2. 구조체 사용을 고려할때마다 클래스가 더 좋은 방법인지 고려한다
  3. 포인터를 오류를 발생할 가능성이 있다. 접근 루틴이나 클래스, 그리고 방어적인 프로그래밍 습관을 사용하여 보호한다
  4. 전역변수를 사용하지 않는다
  5. 만약 전역변수를 꼭! 사용해야 한다면 접근 루틴을 통해서 처리한다. 접근 루틴은 전역 변수가 제공하는 것 이상을 제공한다

 

이 글은 스프링노트에서 작성되었습니다.

by 무위자연 2008. 1. 19. 15:54

순서가 중요한 명령문

ex명령문의 순서가 중요한 자바예제

 data = ReadData();

results = CalculateREsultsFromData(data);

PrintResults(results);

위 코드는 반드시 순서를 지켜야 한다

ex 명령문의 순서가 중요하지만 덜 분명한 자바예제

revenue.ComputeMonthly();

revenue.ComputeQuarterly();

revenue.ComputeAnnual();

코드만으로는 순서의 의존성이 명확하지 않다

ex 명령문의 순서 의존성이 감추어져 있는 VB예제

ComputeMakrteingExpense

ComputeSalesExpense

ComputeTravelExpense

//...

위 코드를 보고서는 순서 의존성을 전혀 알수 없다.

명명문들이 특정한 순서대로 작성되어야 한늬 의존성을 갖고 있을때, 의존성을 분명히 하기 위한 단계를 밟아야 한다.

  • 의존성이 분명하도록 코드를 구성한다.
  • 의존성이 분명하도록 루틴의 이름을 작성한다.
  • 의존성을 분명히 하기 위해서 루틴 매개변수를 사용해라.

    ex 데이터가 순서의 의존성을 말해주는 VB예제

    InitializeExpenseData(expenseData)   //초기화는 무엇보다 먼저 실행되어야 하니까 이름도 초기화를 의미하게 지어주는 게 센스

    ComputeMarketingExpense(expenseData)

    ComputeSalesExpense( expenseData)

    ComputeTravelExpense (expenseData)

    ex 루틴을 expenseData를 입력으로 받고 갱신된 expenseData를 출력으로 반환하는 함수로 변환하여 코드의 순서 의존성이 있음을 보다 명확하게 만든다

    expenseData = InitializeExpenseData(expenseData)  

    expenseData = ComputeMarketingExpense(expenseData)

    expenseData = ComputeSalesExpense( expenseData)

    expenseData = ComputeTravelExpense (expenseData)

  • 의존성이 분명하지 않은 부분은 주석으로 문서화한다
  • assertion이나 오류처리 코드로 의존성을 검사한다

 

순서가 중요하지 않은 명령문

순서가 중요하지 않은 경우에는 일반적인 근거치 법칙, 즉 관련된 작업을 함께 두는 법칙을 따르는 것이 가독성, 성능, 유지 보수에 유리하다

  • 코드를 하향식으로 읽을수 있도록 작성하기.
  • 연관된 명령문 그룹화하기.

 

이 글은 스프링노트에서 작성되었습니다.

by 무위자연 2008. 1. 19. 15:54

조건문은 다른 명령문의 실행을 제어하는 명령문이다. 즉 if, else, case, switch 와 같은 명령문으로 분기된다.

 

if 문

if- then 명령문

  • 일반적인 경우에 대한 코드를 먼저 작성한 다음 특별한 경우를 작성한다
  • 동치에 대해서 정확하게 이동하도록 한다 - >= 대신 > 를 사용하거나 <= 대신 <를 사용하면, 배열에 접근하거나 루프 인데스를 계산할때 하나 차이로 오류(off-by-one)를 만드는 것과 같다.
  • 정상적인 경우를 else가 아니라 if문 다음에 입력한다 - 정상적으로 처리되는 경우를 먼저 작성한다. 이는 결정으로부터의 결과를, 가능한 한 결정을 하는 위치와 가깝게 코드를 작성하는 일반적인 우너칙과 일치한다
  • if 절 다음에는 의미 있는 명령문을 작성한다.

    ex if절이 null인 자바 예제

    if( someTest) ;

    else { // 작업처리

    }

    ex null if절이 변환된 자바예제

    if( !someTest) {작업처리}

  • else절을 고려한다 - 만약 if문이 필요하다면 if-then-else문이 필요 없는지 고려한다. else인 경우를 고려했다는 것을 표시하기 위해 널 절이라도 추가해준다.
  • 정확성을 위해서 else절을 테스트한다
  • if와 else절의 역을 검사한다 - 로직상에 두개의 절 내용이 바뀌는 오류를 확인한다

연속적인 if-then-else 문

case문을 지원하지 않거나 부분적으로만 지원하는 언어에서는 종종 if-then-else테스트를 연속해서 작성하게 될 것이다. 다음은 이것에 대한 지침이다

ex 문자를 분류하기 위해서 일련의 if-then-else를 사용하는 C++예제

if(inputCharacter < SPACE){

 characterType = CharacterType_ControlCharacter;

}

else if {

inputCharacter == '' ||

inputCharacter == ',' ||

inputCharacter == '.' ||

inputCharacter == '!' ||

inputCharacter == '(' ||

inputCharacter == ')'){

chracterType = CharacterType_Punctiation;

}

else if( '0' <= inputCharacter && inputCharacter <= '9') {

characterType = CharacterType_Digit;

}

else if(('a' <= inputCharacter && inputCharacter <= 'z') ||

('A' <= inputCharacter && inputCharacter <= 'Z')){

characterType = CharacterType_Letter;

}

  • 복잡한 테스트를 불린 테스트 호출로 단순화시킨다.

    ex 불린 함수 호출을 사용한 일련의 if-then-else C++예제

    if( IsContorl( inputCharacter))

    {

    chracterType = ChracterType_ControlCharacter;

    }

    else if( IsPunctuation( inputCharacter)){

characterType = CharacterType_Punctuation;

}

else if( IsDigit( inputCharacter)) {

    characterType = CharacterType_Digit;

}

else  if(IsLetter( inputCharacter )){

characterType = CharacterType_Letter;

}

  •  
  • 가장 흔한 경우를 앞에 놓는다 - 가장 흔한 경우를 앞에 놓음으로써, 다른 사람이 일반적인 경우를 찾기 위해서 읽어야 하는 예외적인 상황 처리코드의 양을 최소화할수 있다 그리고 가장 흔한 경우를 찾기 위한 코드 테스트의 수를 최소화하여 효율성을 향상시킨다.

    ex 가장 흔한 경우를 제일 먼저 테스트하는 C++예제

    if(IsLetter( inputCharacter )){ //가장 흔한 테스트가 이제 제일 먼저 수행된다

    characterType = CharacterType_Letter;

    }

    else if( IsPunctuation( inputCharacter)){

    characterType = CharacterType_Punctuation;

    }

    else if( IsDigit( inputCharacter)) {

        characterType = CharacterType_Digit;

    }

    else if( IsContorl( inputCharacter)) //가장 흔하지 않은 테스트가 이제 제일 나중에 실행된다

    {

    chracterType = ChracterType_ControlCharacter;

    }

  • 모든 경우를 다루었는지 확인한다 - 마지막 else 절에는 계획하지 않은 경우를 잡기 위해 오류 메시지나 assertion을 작성하도록 한다.
  • 만약 언어가 지원한다면, if-then-else체인 대신 다른 구조를 사용한다. - case문은 if-then-else체인보다 읽기 쉽고 작성하기 쉽다.

 

CASE문

가장 효율적인 case순서의 선택

  • 알파벳 순으로 또는 숫자순으로 case를 나열한다 - 가독성을 높일수 있다
  • 가장 정상적인 경우를 앞에 놓는다
  • 빈도에 따라서 case를 나열한다

case 문 사용 팁

  • case문이 하는 일을 간단하게 유지한다
  • case문에서 사용하기 위해서 phony 변수를 채우지 않는다 - case문은 쉽게 분류될수 있는 간단한ㅇ 데이터에 대해서 사용되어야 한다. 만약 데이터가 간단하지 않다면, 대신 if-then-else체인을 사용한다. 포니변수(가짜변수)들은 혼란을 야기할수 있기때문에 사용하지 않아야 한다.

ex 포니 case변수를 생성한 자바예제

action = userCommand[0];

switch(action) {

case 'c':

Copy();

break;

case 'd':

DeleteCharacter();

break;

case 'f':

Format();

break;

case 'h':

Help();

break;

//...

default:

HandlingError();

}

이 경우에 case문은 의도와 다르게 동작 가능성이 높다. 사용자가 copy()를 입력할수도 있지만 c로 시작하는 전혀 다른 문장을 입력했을때도 copy()가 실행된다, 올바른 맵핑이 이루어질수 없다. 이런 경우 포니변수 대신에 문자열 비교를 위한 if-then-else체인을 사용하는 것이 좋다

  • 타당한 기본 값을 찾고자 하는 경우에만 dafault절을 사용한다
  • 오류를 검출하기 위해서 default 절을 사용한다
  • C++와 자바에서는 case문의 끝에서 아래로 내려가는 경우를 피한다 - 즉 각 case끝에는 break;를 넣어줘야 한다
  • C++에서는 case문의 끝에서 분명하고 확실하게 아래로 떨어지는것(flow-through)을 확인해야 한다

 

이 글은 스프링노트에서 작성되었습니다.

by 무위자연 2008. 1. 19. 15:54

루프의 종류선택

  • 계수형 루프(coiunted loop) 는 특정한 횟수만큼 수행된다
  • 계속 판단 형 루프(continuously evaluated loop) 얼마나 실행될 것인지를 미리 알지 못하며, 반복할 때마다 중단할지를 검사한다.
  • 무한 루프(endless loop)는 일단 시작되면 끝없이 실행된다.
  • 반복자(iterator)루프는 컨테이너 클래스에 있는 각 요소들에 대해서 한번씩 수행한다

루프의 종류는 유연성에 의해서 차별화된다. 즉, 루프가 지정된 횟수만큼 실행되는지 또는 반복마다 완료 조건을 테스트할 것인지에  따라서 달라진다

 

Loop-With-Exit 루프를 사용하는 시기

종료조건이 루프의 시작이나 끝이 아니라 중간일 경우에 사용

 

이런 루프를 사용할때 주의점

  • 모든 조건들을 한 곳에 입력한다.
  • 조건을 분명히 하기 위해서 주석을 쓴다

 

for 루프를 사용하는 시기

for루프는 지정한 횟수만큼 실행되는 루프가 필요할때 사용한다. 내부적인 루프제어가 필요하지 않은 간단한 작업에 대해서 for루프를 사용한다. 루프제어가 컨테디어에 있는 요소들을 반복하는 것과 같이,

간단한 증가나 감소인 경우에 for를 사용한다. for루프의 핵심은 루프의 맨위에서 설정한 다음, 잊어버리면 된다는 점이다. 루프를 제어하기 위해서 루프내부에서 아무것도 할 필요가 없다.

만약 실행 중에 루프를 빠져나가야 한다면 while 루프를 선호해라.

 

루프의 제어

루프를 다룰때 생기는 문제들 - 잘못된 루프 초기화 , 루프 초기화 생략 , 연산자 혹은 루프와 관련된 다른 변수들의 초기화 생략, 부적절한 중첩, 잘못된 루프 종료, 루프 변수를 증가시키는 것을 잊거나

변수를 잘못 증가시키는 것, 그리고 루프 인덱스에서 배열 요소를 잘못 인덱스 하는 것 등

이것을 방지하기 위한 습관 - 루프 영향을 미치는 요소의 수를 최소화한다. 단순화! 단순화! 단순화! 루프의 내부를 마치 별도의 루틴처럼 처리한다. 될수 있는 한 제어부분을 푸의 밖에서 입력하고 루프 몸체 내에서

실행되어야 하는 조건들을 분명히 한다

루프 진입하기

  •  한 위치에서만 루프에 진입한다
  • 루프가 시작하기 바로 전에 초기화 코드를 입력한다 - 근접성의 원칙에 따른 것. 수정시 오류를 쉽게 피할수 있다. 루프 초기화명령문을 루프 근처에 둔다
  • 무한 루프는 while(true)를 사용한다. - 어떤 프로그래머는 for( ;; )를 대안으로 쓴다
  • 적절할때 for루프를 택한다
  • while루프가 더 적절할때 for루프를 사용하지 않는다

ex while루프를 억지로 for루프 헤더에 밀어 넣은 C++예제

//파일에서 모든 레코드를 읽는 것

for( inputFile.MoveToStart(), recordCount = 0; !inputFile.EndOfFile(); recordCount++) inputFile.GetRecord();

for 루프헤더는 루프 제어 명령문, 즉 루프를 초기화하고 종료하거나 종료의 상황으로 접근하는 명령문을 위해서 사용한다. inputFile.GetRecord()명령문이 루프를 종료상황에서 접근시키고 있지만, recordCount명령문은 그렇지 않다

이 명령문들은 루프의 진행을 제어하지 않는 보조 관리 명령문이다. 루프헤더에 recordCount명령문을 이동시키고, inputFilr.GetRecord()명령문을 밖으로 빼낸 것이 잘못된 것이다. recordCount가 루프를 제어하는 듯한 오해를 만들고 있다.만약 이 경웨 while대신 for를 쓰고 싶다면 루프제어 명령문을 루프헤더에 입력하고 나머지를 모두 밖으로 빼내도록 한다

ex 푸르 헤더에서 논리적인 조건을 자유롭게 사용한 C++예제

recordCount = 0;

for( inputFile.MoveToStart(); !inputFile.EndoOfFile(); inputFile.GetRecord()) recordCount++;

ex while 루프를 적절하게 사용한 C++예제

inputFile.MoveToStart();

recordCount = 0;

while(!inputFile.EndOfFile())

{

inputFile.GetRecord(); recordCount++;

}

 

루프의 중간 부분 처리하기

  • 루프에 있응 명령문들을 둘러싸기 위해서 중괄호를 사용한다
  • 빈 루프를 피한다
  • 루프에서 보고관리 작업들(housekeeping)은 루프의 시작이나 끝에 놓는다. - 루프의 보조관리 작업은 루프가 처리해야 하는 일이 아니라 루프를 제어하기 위한 것이 주된 목적인 표현식, 즉 i= i+ 1 , j++ m같은 표현식이다.
  • 루프가 하나의 기능만 수행해야 한다 - 루프가 한번에 두가지 일을 하기 위해서 사용될수 있다는 사실만으로 두가지 기능을 동시에 수행해도 된다는 것은 정당화되지 못한다. 하나의 루프는 반드시 한가지 일을 수행해야 한다

 

루프의 종료

  • 루프가 종료되는 확인한다 - 루프가 종료되는지 반드시 확인해야 한다
  • 루프 종료조건을 명확하게 한다
  • 루프를 종료하기 위해서 for루프의 인덱스를 조작하지 않는다

    ex루프 인덱스를 조작하는 자바예제

    for(iint i = 0; i < 100; i++){

//코드들

if(...){ i = 100} -> 여기서 조작하고 있다!

//코드들2

}

for루프를 설정할때, 루프 카운터는 사용이 금지된다 루프의 종료조건을 더 제어하기 위해서는 while루프를 사용한다

  • 루프인덱스의 마지막 값에 의존하는 코드를 피한다 - 루프가 끝난 후에 루프의 인덱스값을 사용하는 것은 나쁜 방법이다. 이 값은 루프가 정상적인 종료할때와 비정상종료일때도 다를수 있다. 가독성도 떨어진다.  루프 내에서 적절한 순간에 최종값을 변수에 할당해서 사용하는 것이 좋은 코드이다.쓰기도 읽기도.
  • 안전한 카운터를 사용한다 - 안전한 카운터는 루프가 너무 많이 실행되었는지를 결정하기 위해서 반복될때마다 증가시키는 변수이다. 만약 오류가 발생하면 치명적인 영향을 미치는 프로그램이 있다면, 모든 루프의 종료를 보장하기 위해서 안전한 카운터를 사용할수 있다.

ex 안전한 카운터를 사용할 수 있는 루프에 대한 C++예제

do{

node = node -> Next; //...

}while(node->Next != NULL);

ex 위의 예제에 안전한 카운터를 추가한 예제

safetyCounter = 0;

do{

node = node->Next;

//...

safetyCounter++;

if(safetyCounter >= SAFETY_LIMIT){

Assert("안전한 카운터 오류");

}

//...

}while(node->Next != NULL)

안전한 카운터를 만병통치약이 아니다. 한번에 하나씩 코드에 추가한다. 안전한 카운터는 복잡도를 증가시키고 추가적인 오류를 야기할수 있다. 안전한 카운터가 모든 루프에서 사용되지 않기 때문에, 안전한 카운터를 사요용하는 부분에 있는 루프를 수정할때 빼먹을수 있다.

  • 루프 일찍 종료하기

    • while문에서는 boolean플래그보다는 break문을 사용해라
    • 루프내에 수많은 break문이 산재되는 것을 주의한다
    • 루프의 앞부분에서 테스트를 위한 목적으로 continue를 사용한다 - 각 순회에서 필요없는 부분에 대한 연산을 빼고 진행할수 있다.
    • 언어에서 continue를 지원하지 않는 다면 레이블 break구조를 사용한다.

do {

switch()

{

case A:

if(){

//...

break;   //레이블 break대상이 분명하다

}

}

}while(...);

break와 continue를 신중하게 사용한다.

 

종결점 확인

우리가 루프에 가지는 관심사는 세가지 경우이다. 첫번째, 마지막, 중간에 임의번째이다. 이것이 정상적인 절차를 거친 것인지 확인하기 위한 과정이  효율적인 프로그래머와 아닌 사람이 차이가 난다. 효율적인 프로그래머는 머리에서 돌려보고 손으로 계산해 보는 것이 오류를 찾는데 도움을 준다. 비율적인 프로그래머는 우너하는 조합을 찾을때까지 무작정 실험하는 경향이 있다. 만약 루프가 예상했던 대로 작동하지 않는 다면, 비효율적인 프로그래머는 < 기호를 <=로 바꾼다. 만약 여전히 작동하지 않는 다면, 루프 인덱스에 1을 더하거나 뺀다. 결국 이러한 접근 방법은 우연일뿐만 아니라 더 큰 오류를 만들수 있다. 프로그래머는 프로그램이 왜 정확한지 알지 못하는 결과를 초래한다.

머리로 돌려본다는 것은 여러분이 코드가 어떻게 동작하는지 추측하지 않고 이해하고 있다는 것을 의미한다

 

루프 변수사용

  • 배열과 루프에 한계를 두기 위해서 서수나 열거형을 사용하라
  • 읽기 쉬운 중첩 루프를 작성하기 위해서 의미 있는 변수이름을 사용해라
  • 루프 인덱스 혼선(cross-talk)을 피하기 위해서 의미 있는 이름을 사용해라. - 동일한 이름의 변수를 다르게 사용하는 경우에 혼란을 야긴한다
  • 루프 인덱스 변수의 범위를 루프 자체로 제한하라 - 루프안에서 사용하는 변수를 루프 밖에서 사용하지 마라

 

루프가 얼마나 길어야 할까?에 대한 지침

  • 한군에 볼수 있도록 루프의 길이를 짧게 만들어라
  • 중첩을 3수준으로 제한하라
  • 긴 루프의 내부 루프를 루틴으로 이동하라 - 만약 루프가 잘 설계되었다면, 루프 내에 있는 코드를 루프 내에서 호출하는 하나 이상의 루틴으로 이동시킬수 있다
  • 길이가 긴 루프는 특히 명료하게 작성하라 - 길이가 길수록 복잡해 보인다. - 만약 길이가 짧은 루프를 작성한다면 break와 continue 여러개의 탈줄점(exit) 복잡한 종료조건과 같은 위험한 제어 구조를 사용할수 있다

 

 

루프를 쉽게 작성하는 법 - 안에서부터 밖으로

루프를 쉽게 작성하기 위해서는 다음과 같은 순서로 작성한다

한 문장으로 시작한다. 그 경우를 문장으로 작성한 다음에 들여쓰기하고 그 문장 주위에 루프를 입력한다. 그리고 문장을 루프 인덱스나 계산 표현식으로 바꾼다. 기능이 완성될때까지 이 과정을 반복한다.

작업을 마치고 난 후 필요한 모든 초기화 작업을 추가한다. 간단한 경우에서 시작해서 일반화하는 쪽으로 작업하기때문에, 이러한 코드 작성방식을 안에서 밖으로 진행하는 방식을 생각할수 있다.

 

보험회사에서 사용할 프로그램을 가정해보자. 이 프로그램은 고객의 나이와 성에 따라서 달라지는 보험료 표를 갖고 있다. 해야 할 일은 보험료를 계산하는 루틴을 작성하는 것이다. 목록에 있는 각각의 사람에 대한

보험료를 얻어서 총계에 더하는 루프가 필요하다.

  1. 주석으로 루프의 몸체에 수행해야 하는 단계를 작성한다. 문법의 세부 사항과 루프인덱스, 배열 인덱스 등에 대해서 신경쓰지 않으면, 처리해야 하는 일을 쉽게 적을수 있다.

    1단계 - 루프를 안에서부터 밖으로 작성하는 법 - 표에서 보험료를 읽어온다

    • 총계에 보험료를 더한다
  2. 전체 루프를 작성하지 않는 범위내에서 루프 몸체이 있는 주석을 코드로 변환한다 이 경우에는 한 사람의 보험료를 얻어서 총계에 보험료를 더한다. 추상적인 데이터보다는 실질적이고 구체적인 데이터를 사용한다.

    2단계 - rate = table[]

               totalRate = totalRate + rate;

    이 예제는 table이 보험료 데이터를 보관하고 있는 배열이라고 가정하고 있다. 처음에는 배열의 인덱스에 대해서 걱정할 필요가 없다. rate는 보험료 표에서 선택된 보험료 데이터를 보관하고 있는 변수이고 totalRate는 보험료의 총계를 보관하는 변수이다

  3. 다음으로 table배열에 인덱스를 입력한다

    3단계   - rate = table[census.Age ][ census.Gender ]

                totalRAte = totalRate + rate

    배열은 나이와 성으로 접근되기때문에 인자를 주었다. census가 그룹안에 있는 사람들에 대한 정보를 보관하는 구조체라고 가정하고 있다

  4. 다음 단계는 지금까지 작성한 명령문에 루프를 작성하는 것이다. 이 루츠는 그룹에 있는 사람들에 대한 보험료를 계산해야 하기떄문에 사람들 인덱스로 사용한다

    4단계 For person = firstPerson to lastPerson

                  rate = table [census.Age, census.Gender ]

                 totalRate = totlaRate + rate;

            end For

  5. 이제 적절한 들여쓰기를 한 후에 census변수가 사람마다 달라지므로 적절하게 생성되어야 한다

    For person = firstPerson to lastPerson

                  rate = table [census[person].Age, census[person].Gender ]

                 totalRate = totlaRate + rate;

            end For

  6. 마지막으로 초기화코드를 작성한다. 이경우네는 totalRAte변수가 초기화되어야 한다

    totalRate = 0;

    For person = firstPerson to lastPerson

     

                  rate = table [census[person].Age, census[person].Gender ]

     

                 totalRate = totlaRate + rate;

     

            end For

     

 

 

 

이 글은 스프링노트에서 작성되었습니다.

by 무위자연 2008. 1. 19. 15:53
by 무위자연 2008. 1. 18. 09:10
  • 인수위, 13부2처17청 개편안 확정(종합). 결국 수장의 대우가 달라졌을뿐 나머지는 똑같은거 아닌가? 2008-01-17 07:51:23
  • 오늘까지도 춥네. ㅋ 헙 2008-01-17 07:51:33
  • "올해는 3일 휴일이 6번이나 있단다"http://news.naver.com/main/read.nhn?mode=LS2D&mid=sec&sid1=103&sid2=237&oid=023&aid=0000303973&iid= 2008-01-17 11:40:38
  • codecompldete 읽는중에...만약 루프가 예상했던 대로 작동하지 않는 다면, 비효율적인 프로그래머는 < 기호를 <=로 바꾼다. 만약 여전히 작동하지 않는 다면, 루프 인덱스에 1을 더하거나 뺀다. 결국 이러한 접근 방법은 우연일뿐만 ... 2008-01-17 17:41:18

이 글은 bmwe3님의 2008년 1월 17일의 미투데이 내용입니다.

by 무위자연 2008. 1. 18. 04:34
<object width='380' height='380'><param name='movie' value='http://www.mixbook.com/flash/mixbook_albums.swf?b=32435&k=5aHzydMDoU&mode=production&bid=32435&autoplay=true' /><param name='wmode' value='transparent' /><param name='FlashVars' value='b=32435&k=5aHzydMDoU&mode=production&bid=32435&autoplay=true' /><embed src='http://www.mixbook.com/flash/mixbook_albums.swf?b=32435&k=5aHzydMDoU&mode=production&bid=32435&autoplay=true' FlashVars='b=32435&k=5aHzydMDoU&mode=production&bid=32435&autoplay=true' type='application/x-shockwave-flash' wmode='transparent' width='380' height='380'></embed></object><br/>  <a href='http://www.mixbook.com/books?bid=32435'>View this book full size or get a printed copy at Mixbook</a><br/>
by 무위자연 2008. 1. 17. 15:05
by 무위자연 2008. 1. 17. 15:03