이해하기 쉬운 중간 단계의 추상화를 도입한다

ex. if(node != NULL) then

      while(node.next != NULL) do

node = node.next;

leafName = node.name;

 end while

       else

      leadNAme = ""

end if

위와 같은 루틴을 다음의 명령어로 바꿀수 있다.

leafName = GetLeafName(node)

이는 코드를 더욱 읽기 쉽고 이해하기 쉽게 만든다. 또한, 원래 코드를 포함하고 있는 루틴내에서 복잡성을 줄여준다

 

코드의 중복을 피한다 - 루틴을 작성하는 가장 큰 이유가 코드의 중복을 피하는 것이다. 두 루틴에서 중복된 코드를 빼내어, 공통된 코드의 일반화된 버전을 기본 클래스에 넣은 다음 두개의 특화된 루틴들을 서브클래스로 이동시킨다. 또는 공통된 코드를 별도의 루틴으로 작성한 다음, 두 루틴에 읩력된 부분을 호출하도록 할수도 있다. 한곳에 코드가 있으면 중복을 줄이고 공간을 절약하고 수정도 용이해진다.

 

서브클래싱을 지원한다. - 오버라이드가 가능한 루틴을 간단하게 유지한다면, 서브 클래스 구현에서 오류가 발생할 확률도 줄일수 있다

 

순서를 숨긴다

 

이식성을 향상시킨다.

 

복잡한 boolean 테스트를 단순화한다.

 

성능을 개선한다 - 여러 곳에 있는 코드 대신에 한곳에 있는 코드를 최적화시킨다. 한 곳에 모아놓으면 비효율적인 부분을 개선하기 쉬워진다.

 

모든 루틴들이 작다는 것을 보장하기 위해서 루틴을 작성하는 것은 아니다. 문제는 어떻게 알고리즘을 구성하는가 이지 얼마나 코드가 짧은가와는 상관없는 일이다.

 

루틴을 작성하기에는 너무 단순해보이는 연산

효율적인 루틴을 작성하는데 가장 큰 정신적 장애물 중 하나는 간단한 목적을 위해서 간단한 루틴을 작성하는 것을 꺼리는 마음이다. 필자의 경험상 2-3줄의 짧은 루틴도 매우 유용하다. 루틴의 장점중에 하나는 가독성을 높이는 것이다.

ex. 계산을 수행하는 의사코드 예제.

points = deviceUnits * (POINTS_PER_INCH) / DeviceUnitsPerInch())

함수로 변환된 계산을 의사코드로 작성한 예제

function DeviceUnitsToPoints (deviceUnits Integer) : Integer

DeviceUnitsToPoints = deviceUnits * (POINTS_PER_INCH / DeviceUnitsPerInch())

end function

계산함수를 호출하는 의사코드

points = DeviceUnitsToPoints(deviceUnits)

여기서 특정환경에서 DeviceUnitsPerInch는 0을 리턴해야 한다면 함수만 수정해주면 된다 아래처럼.

function DeviceUnitsToPoints (deviceUnits Integer) : Integer

if(DeviceUnitsPerInch != 0)

DeviceUnitsToPoints = deviceUnits * (POINTS_PER_INCH / DeviceUnitsPerInch())

else

DeviceUnitsToPoints = 0;

endif

end function

만일 이 코드를 12곳에서 사용했다면 3 * 12만큼 수정 할 것을 3줄로 줄여준 것이다.

 

루틴수준에서의 설계.

루틴내에서 혹은 루틴간에 응집성이 있어야 한다. 순차적 응집성. 통신적 응집성. 일시적 응집성 등을 고려해야 한다. 한 루틴이 if와 case문의 연속으로 구성되어 있고 각 분기문에서 명령어 처리만 하는 경우에 기술적인 용어로 "event handler"라고 한다.

 

좋은 루틴 이름

루틴의 좋은 이름은 루틴이 하는 모든 것을 분명하게 해준다.

루틴이 하는 모든것을 표현하라. - 예를 들어 전체보고서를 계산하고 출력파일을 연다면 computeReportTotal()이 아니라 ComputeReportTotalsAndOpenOutputFile()이 적절한 이름이 되겠다. 다만 한번에 보다 직접적인 일을 하는 루틴을 작성하는 것이 좋다

의미가 없거나 모호하러나 뚜렷한 특징이 없는 동사를 피해라 - 어떤 동사들은 포괄적이고 유연해서 너무 많은 뜻을 포함하기도 한다. HandleCalcualtion(), PerfomService(), OutputUser(), ProcessInput() etc. 루틴의 역할이 모호하여 이름또한 모호한 경우 루틴을 재구성해서 뚜렷한 목적과 이름을 붙여준다.

루틴의 이름을 숫자만으로 구분하지 마라.

필요한 길이만큼 루틴의 이름을 만들어라.- 이름은 너무 길어도 너무 짧아도 루틴을 정확하게 이해하기 힘들다

함수의 이름을 지을때, 리턴값에 대한 설명을 사용해라.

프러시저의 이름을 지을때, 확실한 의미를 갖는 동사 다음에 객체를 사용해라. - 프로시저의 이름은 프로시저가 무엇을 하는지를 반영해야 하기때문에, 객체에 대한 연산은 동사 + 객체이름을 갖는다. ex.PrintDocument(), CalcMonthlyRevenues(), CheckOrderInfo() etc.

반의어를 정확하게 사용하라. first/last 와 같이 서로 반대되는 짝들은 쉽게 이해가 된다.

ex. 널리 쓰이는 반의어

add/remove   increment/decrement   open/close   begin/end   insert/delete   show/hide   create/destroy   lokc/unlock   source/target   first/last

min/max   start/stop   get/put   next/previous   up/down   get/set   old/new

공통적인 연산을 위한 규약을 만들어라.

 

루틴길이에 대한 문제.

정도의 차이는 있겠지만 200줄은 넘기지 않는 것이 좋을 듯하더이다.

 

루틴의 매개변수를 사용하는 방법

매개변수를 입력-수정-출력 순서로 입력한다. : 매개변수를 무작위로 혹은 알파벳순으로 정렬하는 대신, 입력만 가능한 것을 첫번째로, 입출력이 가능한 것을 두번째로, 출력만 가능한 것을 세번째로 나열한다.  - 순서는 필자에게 유용한 순서규약이고 각자가 자신에게 가장 명확한 규약을 만들어 써도 괜찮다

고유한 in과 out키워드 생성을 고려해보라.: 이런 키워드를 사용한다면 좀더 의미가 명확해 질수 있다.

만약 여러 루틴들이 유사한 매개변수들을 사용한다면, 유사한 매개변수들을 일관된 순서로 입력하라 : 루틴 매개변수의 순서는 기억에 도움이 된다. 예를 들어 fprintf()는 printf()와 첫번째 인자가 파일이라는 것을 제외하고는 아주 유사하다. fputs()은 마지막 인자로 파일을 추가한다는 점을 제외하고 puts()과 동일하다

모든 매개변수들을 사용하라 - 입력받은 매개변수중에 사용하지 않은 변수는 인터페이스에서 제거해버려라.

상태나 오류변수를 마지막에 입력한다. - 오류 역시 출력만 하는 변수임으로 마지막에 위치하는 것이 좋다

루틴의 매개변수를 작업용(working) 변수로 사ㅓ용하지 말라. - 루틴에 전달된 매개변수들을 작업용 변수로 사용하는 것은 좋지 않다.

ex. 입력매개변수를 부적절하게 사용하는 자바예제

int Sample(int inputVal)

{

inputVal = inputVal * CurrentMultiplier(inputVal);

inputVal = iinputVal + CurrentAdder(inputVal);

//...

return inputVal;      //이미 inputVal은 입력된 값을 가지고 있지 않다!!!

}

 

ex. 입력매개변수를 적절하게 사용하는 자바예제

int Sample(int inputVal)

{

int workingVal = inputVal;

workingVal = workingVal * CurrentMultiplier(workingVal );

workingVal = workingVal + CurrentAdder(workingVal );

//...중간에 어떤 상황에서도 inputVal의 값을 이용할수 있게 된다.

return workingVal ;     

}

매개변수에 대한 인터페이스 가정을 문서화하라.

루틴 매개변수의 수를 7개정도로 제한하라 : 7은 사람들의 이해력에 대한 매직넘버이다.

매개변수에서 사용할 입력, 수정, 출력 이름 규약을 고려하라. - 입력, 수정, 출력 매개변수를 구분하는 것이 중요하다면 그것을 식별할수 있는 식별자를 두어라.

ex.i_, m_ , o_ , 혹은 imput_ , modify_ , output_ , etc.

루틴이 인터페이스 추상화를 유지해야 할 필요가 있는 변수나 객체를 전달하라.

이름(named)매개변수를 사용하라 - 잘 모름-

실질적인 매개변수가 형식적인 매개변수와 일치하는지 확인하라

 

함수사용시 특별히 고려해야 할 사항들

함수를 사용할때와 프로시저를 사용할때를 구별하라 - 함수는 입력 매개변수들만을 취해서 함수자체를 통해 오직 하나의 리턴값을 가진다. 반면에 프로지서는 입력, 수정, 출력 매개변수들을 원하는 만큼 취해서 상태값을 바꿔준다. 루틴의 일차적인 목적이 함수의 이름에서 가르키고 있는 값을 반환하는 경우에는 함수를 사용하라. 그렇지 않다면 프로시저를 사용해라.

 

함수의 리턴값 설정 - 함수의 리턴값이 잘못된 값일 경우(난리난다)를 줄이는 다음의 지침을 기억하자

가능한 모든 리턴 경로를 검사하라

지역데이터에 대한 참조나 포인터를 리턴하지 말라.

 

  • 매크로 , inline 부분 생략

 

7장의 요점

  • 루틴을 작성하는 가장 중요한 이유는 프로그램의 지적인 관리성을 향상시키기 위한 것이다. 공간 절약은 중요하지 않다. 가독성, 신뢰성, 수정성의 향상이 더 좋은 이유이다.
  • 때떄로 별도의 루틴의 이득이 크지 않을수 있다
  • 루틴의 이름은 루틴의 품질이다. 정확한 이름의 정확한 루틴이 좋은 루틴이다
  • 함수는 그 주된 목적이 함수의 이름에 묘사된 특정한 값을 리턴하는 것일때에만 사용한다.
  • 신중한 프로그래머는 매크로를 주의 깊에 사용하여 최후의 수단으로 쓴다.

 

 

 

 

 

 

 

 

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

by 무위자연 2008. 1. 19. 17:06