[CS] Code Complete 정리

Posted by qwlake on March 30, 2021
  • Code Complete
    • 고품질 코드 작성
      • Class
        • 추상 데이터형(abstract data type; ADT), 132p

          클래스와의 차이

          • 추상 데이터형에 상속과 다형성을 더한 것이 클래스

          장점

          • 구현 세부 사항을 감출 수 있다.
          • 변경이 전체에 영향을 미치지 않는다.
          • 인터페이스가 더 많은 정보를 제공하도록 만들 수 있다.
          • 성능을 향상시키기 쉽다.
          • 프로그램이 명백하게 정확해진다.
          • 프로그램의 가독성이 높아진다.
          • 전체 프로그램에 데이터를 넘길 필요가 없다.
          • 저수준 구현 구조체 대신 현실 세계의 개체를 다룰 수 있다.

          사용 시 원칙

          • 전형적인 저수준 데이터형을 저수준 데이터형이 아닌 ADT로 만들거나 사용하라.
          • 파일과 같은 일반적인 객체를 ADT로 취급하라.
          • 간단한 객체도 ADT로 취급하라.
          • ADT가 저장된 매체와 독립적으로 ADT를 참조하라.
        • 좋은 클래스 인터페이스, 140p

          좋은 추상화

          • 클래스 인터페이스가 일관된 추상화 수준을 갖도록 한다.
          • 클래스가 구현하고 있는 추상화가 무엇인지 이해해야 한다.
          • 서로 반대되는 기능을 갖는 서비스 쌍을 제공하라.
          • 관련이 없는 정보를 다른 클래스로 옮겨라.
          • 가능하면 인터페이스를 의미론적이기보단는 프로그래밍적으로 만들어라.

            의미론적: 주석으로 설명 프로그래밍적: Assert 등 프로그래밍적인 인터페이스 요소로 강제화

          • 코드 변경 시 인터페이스의 추상화가 망가지지 않도록 주의한다.
          • 인터페이스 추상화에 맞지 않는 공개 멤버를 추가하지 말라.
          • 추상화와 응집도를 함께 고려하라.

          좋은 캡슐화

          • 캡슐화는 추상화보다 더 강력한 개념.
          • 클래스와 멤버의 접근성을 최소화하라.
          • 멤버 데이터를 public으로 노출하지 말라.
          • 클래스 구현 세부 사항을 클래스의 인터페이스에 입력하지 말라.
          • 클래스의 사용자를 가정하지 말라.
          • 프렌드 클래스를 피하라(?).
          • 어떤 루틴이 공개 루틴만 사용한다고 해서 public 인터페이스에 두지 말라.
          • 코드를 작성할 때의 편의성보다 가독성이 높은 코드를 작성하라.
          • 캡슐화의 의미론적인 위반을 각별히 주의하라.

            클래스 A의 PerformFierstOperation() 루틴이 InitializeOperations() 루틴을 자동으로 호출하는 것을 알고 있기 때문에 클래스 A의 InitializeOperations() 루틴을 호출하지 않는다.

            위 예시의 문제는 클라이언트의 코드가 private 구현에 의존하고 있다는 점이다. 따라서 인터페이스 문서만으로 클래스를 어떻게 사용해야 하는지 알 수 없다. 결국 클래스가 어떻게 구현되어 있는지 확인해야 하는 상황이 발생한다.

          • 지나치게 밀접한 결합을 주의하라.
        • 설계와 구현 문제, 152p

          포함(has a) 관계

          • 포함을 통해서 ‘has a’를 구현하라.
          • 최후의 수단으로 비공개 상속을 통해서 ‘has a’를 구현하라.
          • 약 7개 이상의 데이터 멤버를 포함하는 클래스를 주의하라.

          상속(is a) 관계

          • 공개 상속을 통해 ‘is a’를 구현하라.
          • 상속을 고려해서 설계하고 문서화하라. 그게 아니라면 상속을 금지하라.
          • 리스코프 치환 원칙(LSP)을 따르라.
          • 상속받고 싶을 때만 상속받게 하라.
          • 오버라이트가 불가능한 멤버 함수를 ‘오버라이드’하지 말라.
          • 공통으로 사용되는 인터페이스와 데이터, 해우이를 상속 단계에서 가능한 한 가장 높은 곳으로 옮겨라.
          • 인스턴스가 하나뿐인 클래스를 의심하라.
          • 파생 클래스가 하나뿐인 기본 클래스를 의심하라.
          • 루틴을 오버라이드했는데 파생된 루틴 내부에서는 아무것도 하지 않는 클래스들을 의심하라.
          • 깊은 상속 구조를 피하라.
          • 광범위한 타입 검사보다 다형성을 택하라.
          • 모든 데이터를 보호가 아닌 비공개로 만들어라.

          멤버 함수와 데이터

          • 클래스에 가능한 한 적은 수의 루틴을 유지하라.
          • 원하지 않는 멤버 함수와 연산자가 암묵적으로 생성되지 않도록 하라.
          • 클래스에서 호출되는 루틴의 수를 최소화하라.
          • 다른 클래스에 대한 간접적인 루틴 호출을 최소화하라.
          • 일반적으로 클래스가 다른 클래스와 협력하는 정도를 최소화하라.

          생성자

          • 가능하다면 모든 멤버 데이터를 모든 생성자에서 초기화하라.
          • 비공개 생성자를 사용해 싱글턴 속성을 구현하라.
          • 다른 사실이 증명될 때까지 얕은 복사보다 싶은 복사를 택하라.
        • 클래스를 작성하는 이유, 162p

          • 현실 세계의 객체를 모델링한다.
          • 추상 객체를 모델링한다.
          • 복잡성을 줄인다.
          • 복잡성을 고립시킨다.
          • 구현 세부 사항을 숨긴다.
          • 변경의 효과를 제한한다.
          • 전역 데이터를 숨긴다.
          • 매개변수 전달을 간소화한다.
          • 중앙 집중 관리한다.
          • 코드 재사용을 돕는다.
          • 프로그램 전체를 고려한다.
          • 연관된 기능을 패키지로 구성한다.
          • 특정한 리팩터링을 수행한다.
          • God 클래스를 생성하지 말라.
          • 관련이 없는 클래스를 제거하라.
          • 동사를 뒤에 붙는 클래스를 피하라.
    • Variable
      • 변수 사용 시 고려해야 할 사항, 255p
        • 변수 초기화 가이드라인, 255p
          • 변수를 선언할 때 초기화한다.
          • 변수가 처음 사용되는 곳 근처에서 초기화한다.
          • 이상적으로 각 변수가 처음 사용되는 곳 가까이에서 변수를 초기화하고 정의한다.
          • 가능하다면 final이나 const를 사용한다.
          • 카운터와 누산기(i, j, sum, total 등 변수)를 재사용할 때는 초기화 해야 한다.
          • 클래스의 멤버 데이터를 생성자에서 초기화한다.
          • 다시 초기화해야 할 필요가 있는지 검사한다.
          • 이름 상수를 한 번 초기화하고 실행 코드로 변수를 초기화한다.
          • 모든 변수를 자동으로 초기화하는 컴파일러 설정을 사용한다.
          • 컴파일러의 경고 메시지를 활용한다.
          • 입력 매개변수의 유효성을 검사한다.
          • 메모리 접근 도구를 사용해 부적절한 포인터를 검사한다.
          • 프로그램을 시작할 때 메모리를 초기화한다.
        • 범위, 259p

          변수에 대한 참조를 지역화하라.

          변수의 수명을 가능한 한 짧게 유지한다.

          범위를 최소화하기 위한 일반적인 가이드라인

          • 루프에서 사용되는 변수는 루프를 포함하고 있는 루틴의 시작이 아니라 루프 바로 앞에서 초기화한다.
          • 변수를 사용하기 바로 전까지 변수에 값을 할당하지 않는다.
          • 연관된 명령문을 그룹화한다.
          • 연관된 명령문 그룹을 별도의 루틴으로 나눈다.
          • 처음에는 범위를 최대한 제한하고 필요한 경우에만 변수의 범위를 늘린다.
        • 지속성, 267p
          • 데이터가 예상한 것보다 오래 존재하는 경우 문제가 될 수 있음
          • 변수를 사용하기 전에 초기화하고, 사용 후에는 반환하는 습관을 들여라.
          • 가능한 한 그 범위는 짧게 유지하는 것이 좋다.
        • 결합 시점, 268p

          변수와 값이 결합되는 시점은 아래와 같고, 결합 시점이 이를 수록 유연성이 낮아지지만 복잡성이 줄어듬. 적절한 선택 필요.

          1. 코드 작성 시간(매직 넘버 사용)
          2. 컴파일 시간(이름 상수 사용)
          3. 로드 시간(윈도우 레지스트리 파일이나 자바 속성 파일과 같은 외부의 소스로부터 값을 읽어 들임)
          4. 객체 생성 시간(윈도우가 생성될 때마다 값을 읽어 들임)
          5. 적시에(윈도우가 그려질 때마다 값을 읽어 들임)
        • 변수를 한 가지 목적으로만 사용하기, 271p
          • 각 변수를 한 가지 목적만을 위해서 사용하라.
          • 숨은 의미가 있는 변수를 피하라

            pageCount 변수에 있는 값이 출력된 페이지 수를 나타낼 때, -1은 오류가 발생했다는 것을 의미 → WRONG

            customerId 변수가 고객 번호를 나타낼 때, 500,000보다 크면 연체된 계정의 수를 구하기 위해서 500,000을 뺀다. → WRONG

            bytesWritten 변수가 출력 파일에 기록된 바이트 수를 나타낼 때, 이 값이 음수이면 출력 파일에 사용된 디스크의 수를 가리킨다. → WRONG

          • 선언된 모든 변수를 사용하는지 확인하라.
      • 변수 이름의 기능, 275p
        • 좋은 이름을 짓기 위한 고려 사항

          최적의 이름 길이

          • 10~16, 9~15 사이의 길이가 적당
          • 금발 소녀와 세 마리 곰 스타일로 짓는 게 도움 될 것이다.

            numberOfPeopleOnTheUsOlympicTeam: Too long

            not: Too short

            numTeamMembers: good

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

          • 전역 네임스페이스에 있는 변수에는 한정자를 사용하라.

          변수 이름의 계산값 한정자

          • Total, Sum 등과 같은 한정자로 변수의 이름을 만들 경우 이름 끝에 한정자 붙여라. ex) revenueTotal, expenseTotal
            • 단, num은 맨 앞에 붙일것. ex) numCostomers

          일반적인 변수명의 반의어

          • 반의어를 정확하게 사용할것.
            • source/target
            • source/destination
            • next/previous
            • visible/invisible
        • 특정 타입의 데이터 이름 짓기

          반복문 인덱스 이름

          • 관습적으로 i, j, k 사용

            예외. 의미 있는 변수명을 사용해야할 때

            • 반복문 외부에서 사용해야 한다면 recordCount와 같이 의미 있는 이름으로 사용할것.
            • 반복문이 길어질 때
            • 반복문이 중첩될 때

          상태 변수 이름

          • 의미가 애매한 flag와 같은 변수 사용하지 말 것.

          임시 변수 이름

          • 임시 변수를 조심하라

          불린 변수 이름

          • 전형적인 불린 변수의 이름을 기억한다.
            • done
            • error
            • found
            • success / ok
          • 참이나 거짓의 의미를 함축하는 불린 변수의 이름을 사용한다.
            • status X / statusOK O
            • sourceFile X / sourceFileAvailable
          • 긍정적인 불린 변수 이름을 사용한다.
            • notFound X / found O

          열거형의 이름

          • Color_, Month_와 같은 접두사를 활용하면 구분이 쉽다.

          상수 이름

          • 상수 이름은 상수가 가르키는 숫자보다는 상수가 표현하는 대상이 더 추상적이어야 한다.

            ex. FIVE X / CYCLES_NEEDED O

        • 읽기 쉬운 짧은 이름

          일반적인 축약어 가이드라인

          • 표준 축약어 사용(사전)
          • 불필요한 모음 제거(computer → cmptr, integer → intgr)
          • 관사와 접속사 제거(and, or, the 등)
          • 각 단어의 첫 번째 문자나 처음 문자 몇 개를 사용
          • 첫 번째나 두 번째, 세 번째에서 일관성 있게 단어를 자른다
          • 각 단어의 첫 번째와 마지막 문자를 유지
          • 이름에서 가장 중요한 단어를 최대 세 단어까지 사용
          • 불필요한 접미사 제거(ing, ed 등)
          • 각 음절에서 가장 뚜렷한 소리를 유지
          • 변수의 의미를 변경하지 않도록
          • 글자 수가 적절히 줄어들 때까지 이 과정 반복

          음성 축약

          • skating → ska8ing, highlight → hilite

          축약어에 대한 의견

          • 단어에서 한 문자를 없애는 방법으로 축약하지 않는다.
          • 일관성 있게 축약한다.
          • 발음할 수 있는 이름을 만든다.
          • 잘못된 발음을 유발할 수 있는 조합을 피한다.
          • 이름 충돌을 해결하기 위해서 유의어 사전을 사용한다.
          • 매우 짧은 이름은 코드 내 변환 테이블을 사용하여 문서화한다.
          • 모든 축약어를 프로젝트 수준의 ‘표준 축약어’ 문서에 기록한다.
          • 이름은 코드를 작성하는 사람보다 읽는 사람에게 더 중요하다는 점을 기억한다.
        • 피해야 할 변수 이름

          • 오해의 소지가 있는 이름이나 축약어를 피한다.
          • 유사한 의미가 있는 이름을 피한다.

            ex. (recordNum / numRecord), (fileNumber / fileIndex)

          • 이름은 유사하지만 의미가 다른 변수를 피한다.

            ex. clientRecs vs clientRps X → clientRecords vs clientReports O

          • 이름에 숫자를 넣는 것을 피한다.
          • 이름에 철자가 틀린 단어가 없도록 한다.
          • 영어에서 일반적으로 잘못 표기되는 단어를 피한다.
          • 대소문자만으로 변수의 이름을 구분하지 않는다.
          • 여러 가지 자연어를 사용하지 않는다.
          • 표준 데이터형과 변수, 루틴의 이름은 사용하지 않는다.
          • 변수가 표현하는 것과 전혀 관련이 없는 이름을 사용하지 않는다.
          • 읽기 어려운 문자를 포함하는 이름을 피한다.
    • 개발 프로세스

      소프트웨어 정신에 대한 주제, 904p

      • 최대한 간결하게 → 사람이 읽는 것임
      • 경고를 무시하지 말자
      • 문제를 해결하기 위해 세부 구현 사항은 숨겨야 한다.

        최상위 수준의 코드에서 “i는 직원 파일에서 얻은 레코드의 인덱스를 나타내며 잠시 후에는 고객의 계정 파일의 인덱스를 나타내기 위해서 사용된다.” 와 같은 주석 덩어리를 포함하여서는 안 된다.

      24.소프트웨어 개발 프로세스