독서일지/클린 코드

클린 코드 독서일지 - Day 39

Sadie Kim 2023. 12. 17. 16:58

냄새와 휴리스틱

다양한 코드 냄새(코드를 좋지 않게 짠 경우)와 코드를 짜면서 사용하는 기교, 휴리스틱을 소개한다.
아래의 경우들은 모두 바람직하지 않은 관습들의 나열임

주석

C1: 부적절한 정보

소스 코드 관리 시스템, 버그 추적 시스템, 이슈 추적 시스템 등 다른 시스템에 저장할 정보는 주석으로 적절치 않음

C2: 쓸모 없는 주석

오래된 주석, 엉뚱한 주석, 잘못된 주석은 더 이상 쓸모가 없음
=> 쓸모 없는 주석은 코드와 무관하게 따로 놀며 코드를 그릇된 방향으로 이끎

C3: 중복된 주석

코드만으로 충분한데 구구절절 설명하는 주석은 불필요함

C4: 성의 없는 주석

문법과 구두점을 올바로 사용하고, 간결하고 명료하게 작성한다.

C5: 주석 처리된 코드

주석 처리된 코드는 아무도 그 쓰임을 모르며, 매일매일 낡아감
소스 코드 관리 시스템이 기억하기 때문에 삭제해도 된다.

환경

E1: 여러 단계로 빌드하는 것

빌드는 간단히 한 단계로 끝나야 하며, 한 명령으로 빌드할 수 있어야 한다.

E2: 여러 단계로 테스트하는 것

모든 단위 테스트는 한 명령으로 돌려야 한다.

함수

F1: 너무 많은 인수

함수에서 인수 개수는 작을수록 좋다.

F2: 출력 인수

출력 인수는 직관적이지 못하다. 일반적으로 독자는 인수를 입력으로 간주
출력 인수 대신 함수가 속한 객체의 상태를 변경하는 방법을 쓰자.

F3: 플래그 인수

플래그 인수는 함수가 여러 기능을 수행한다는 증거 -> 혼란을 초래하므로 피해야 마땅

F4: 죽은 함수

아무도 호출하지 않는 함수는 삭제한다.

일반

G1: 한 소스 파일에 여러 언어를 사용하는 것

소스 파일 하나에 언어 하나만 사용하는 방식이 가장 좋다.
-> 소스 파일에서 언어 수와 범위를 최대한 줄여야 함

G2: 당연한 동작을 구현하지 않는 것

최소 놀람의 원칙에 의거해 함수나 클래스는 다른 프로그래머가 당연하게 여길 만한 동작과 기능을 제공해야 함

당연한 동작을 구현하지 않으면 코드를 읽거나 사용하는 사람이 함수 이름만으로 함수 기능을 직관적으로 예상하기 어렵다.

G3: 경계를 올바로 처리하지 않는 것

코드의 모든 경계 조건과 구석진 곳에서 잘 동작하는지를 증명해야 한다.
-> 모든 경계 조건을 찾아내고 모든 경계 조건을 테스트하는 테스트 케이스를 작성해야 함

G4: 안전 절차 무시

안전 절차를 무시하면 위험함.
-> serialVersionUID를 직접 제어한다든지, 컴파일러 경고를 끈다든지, 실패하는 테스트 케이스를 미뤄두는 태도는 위험함

G5: 중복

코드에서 중복이 있으면 안 됨 -> 중복을 발견할 때마다 추상화할 기회로 간주하라.

  • 똑같은 코드가 여러 차례 나올 경우 : 중복을 간단한 함수로 교체
  • 여러 모듈에서 일련의 switch/case나 if/else 문으로 똑같은 조건을 거듭 확인하는 경우 : 중복을 다형성으로 대체
  • 알고리즘이 유사하나 코드가 서로 다른 중복 : TEMPLATE METHOD 패턴이나 STRATEGY 패턴으로 중복 제거

G6: 추상화 수준이 올바르지 못할 경우

모든 저차원 개념은 파생 클래스(자식 클래스)에 넣고, 고차원 개념은 기초 클래스(부모 클래스)에 넣는다.(기초 클래스는 구현 정보에 무지해야 함)

고차원 개념과 저차원 개념을 섞어서는 안 된다.

G7: 기초 클래스가 파생 클래스에 의존하는 것

추상화의 가장 흔한 이유는 고차원 기초 클래스 개념을 저차원 개념으로부터 분리해 독립성을 보장하기 위해서임
=> 기초 클래스가 파생 클래스를 아예 몰라야 마땅함.

G8: 과도한 정보

잘 정의된 모듈은 인터페이스가 아주 작으며, 잘 정의된 인터페이스는 많은 함수를 제공하지 않음
클래스가 제공하는 메서드 수는 작을수록 좋으며, 함수가 아는 변수 수도 작을수록 좋고, 클래스에 들어있는 인스턴스 변수 수도 작을수록 좋다.
=> 정보를 제한하면 결합도가 낮아진다.

G9: 죽은 코드

죽은 코드 : 실행되지 않은 코드. (ex: 불가능한 조건을 확인하는 if문, throw문이 없는 try문에서 catch 블록)
죽은 코드는 설계가 변해도 제대로 수정되지 않아 발견하는 대로 시스템에서 제거해야 함

G10: 수직 분리

변수와 함수는 사용되는 위치에 가깝게 정의하며, 비공개 함수는 처음으로 호출한 직후에 정의한다.

G11: 일관성 부족

어떤 개념을 특정 방식으로 구현했다면 유사한 개념도 같은 방식으로 구현한다.
한번 선택한 표기법을 그대로 따른다.

G12: 잡동사니

비어 있는 기본 생성자, 아무도 사용하지 않는 변수, 아무도 호출하지 않는 함수, 정보를 제공하지 못하는 주석 등 불필요한 코드들을 모두 제거해야 한다.

G13: 인위적 결합

서로 무관한 개념을 인위적으로 결합하지 않는다. (ex: enum을 특정 클래스에 속하도록 만드는 등)
=> 함수, 상수, 변수를 선언할 때는 올바른 위치를 고민한다.

G14: 기능 욕심

클래스 메서드는 다른 클래스의 변수와 함수에 관심을 가져서는 안 됨
-> 메서드가 다른 객체의 내용을 조작한다면 그 객체 클래스의 범위를 욕심내는 것임
어쩔 수 없는 경우를 제외하면, 한 클래스에서 다른 클래스의 속사정을 알아야 하는 기능 욕심은 제거하는 편이 좋다.