[Code Complete 2] 2부 - 고품질 코드 작성
5. 구현 설계
5. 구현 설계
5.1 설계의 어려움
5.2 핵심 설계 개념
5.3 설계 빌딩 블록: 발견적 학습
5.4 설계 실천법
5.5 잘 알려진 방법론에 대한 의견
6. 클래스 다루기
6.1 클래스의 토대: 추상 데이터형(ADT)
6.2 좋은 클래스 인터페이스
6.3 설계와 구현 문제
6.4 클래스를 작성하는 이유
6.5 프로그래밍 언어와 관련된 이슈
6.6 클래스를 넘어서: 패키지
7. 고급 루틴
7.1 루틴을 작성하는 이유
7.2 루틴 수준의 설계
7.3 좋은 루틴 이름
7.4 루틴의 길이에 대한 문제
7.5 루틴 매개변수 처리
7.6 함수를 사용할 때 특별히 고려해야 할 사항
7.7 매크로 루틴과 인라인 루틴
8. 방어적인 프로그래밍
8.1 잘못된 입력으로부터 프로그램 보호
- 기본적으로 모든 인풋을 검사할 것.
- 쓰레기를 넣으면 쓰레기가 나온다 (X)
- 쓰레기를 넣으면 아무것도 안 나온다 / 에러를 낸다, 쓰레기 자체가 허용되지 않는다 (O)
8.2 어설션
- 오류 처리는 예상이 되었고 코드에 의해 처리되어야 하는 것, 어설션은 절대 발생하면 안 되는 조건을 위해 사용한다.
- 프로그램이 클수록 유용하다
- 선행/후행 조건을 문서화하는 용도로 사용하기 좋다.
- 개발/유지보수를 목적으로 한다. 릴리즈용이 아니다!
8.3 오류 처리 기법
- 오류의 성격에 따라 적절한 처리방식이 다 다를 것. 선택적으로 사용하면 된다.
- 중립값, 다음 값/이전과 동일한 값/가장 가까운 값, 오류 코드 등을 리턴하기
- 에러 파일에 기록하기, 아예 종료하기 등
- 오류 처리를 하이레벨에서 할지, 로우레벨에서 할지 결정하고, 일관성 있게 따르라.
8.4 예외
- 무시되어서는 안 되는 오류를 위해 사용한다. 절대로 일어나서는 안 되는 경우.
- 생성자와 소멸자에서는 쓰지 말 것.
- 신중하게 사용하면 복잡성을 줄일 수 있고, 무분별하게 쓰면 이해하기가 거의 불가능해진다. 그러니 다른 방법이 정말 없는지 한번 더 생각해 보자.
8.5 오류로 인한 손상을 막기 위한 방책
- validation!
8.6 디버깅 보조 도구
- 공격적인 프로그래밍 : 릴리즈에서는 젠틀하게, 개발에서는 파괴적으로.
- 전처리기, make 등을 잘 활용하기.
8.7 제품 코드를 얼마나 방어적으로 프로그래밍할 것인지 정하기
- 눈에 띄는 디버깅 멧지ㅣ/충돌코드 코드 지우기.
- 그럼에도 치명적인 오류를 감지해야 한다면 “젠틀하게” 종료될 수 있게 만들어 남겨두기.
8.8 방어적인 프로그래밍에 대해서 한 번 더 고민하기
- 방어를 위한 코드가 너무 많으면 트레이드오프가 있다 당연히.
9. 의사코드 프로그래밍 프로세스
9.1 클래스 및 루틴 개발 단계 요약
- 클래스 작성 : 뭘 숨길거냐 ? 뭘 추상화할거냐 ? 핵심 인터페이스는 뭐냐 ? 중요한 멤버 변수는 ?
- 함수 작성 : 생각했던 함수 구현 - 만들다가 필요해지는 함수도 구현 - 때로 문제 발생 및 해결 - 구현..
- 점검과 테스트
9.2 전문가를 위한 의사코드
- 프로그래밍 문법적인 요소는 아예 배재하고, 한글/영어로만 쭉 써본다. ‘이정도면 코드로 바로 변환해도 되겠다’ 싶을 정도로 낮은 수준에서 쓴다.
- 코드를 보지 않아도 설계 검토가 쉬워지고, 변경이 쉬워진다. 그리고 의사코드가 바로 주석이 된다.
9.3 PPP를 이용한 루틴 구현
- 의사코드 작성 > 코드로 변환 > 머릿속에서 검사 > 컴파일 > 반복
9.4 PPP 대안
- TDD, 리팩토링 계약에 의한 설계 등
인상깊은 구절
- 루틴이 맞다는 확신이 되기 전까지는 컴파일을 늦추라. ‘한번만 더 컴파일하면 제대로 작동하게 할 수 있을거야’라는 생각은 조급하게 코드를 만들게 되고 실수하기 쉬워져 결국 시간이 더 걸린다.
- 여러분이 생각한 첫 번째 설계로 결정하지 않는다. 의사코드에서 여러 접근법을 이용해 반복하고 작성한다.