CS/클린코드(cleancode)

17장. 냄새와 휴리스틱 (1)

rachel_13 2022. 12. 11. 17:03

# 휴리스틱(heuristics)

사전적 의미 : "경험적인, 스스로 발견하게 하는"

복잡한 문제는 복잡하게 생각하지말고, 단순하게 생각하는 것부터 시작하자. Bottom-Up 방식처럼 단순한 논리적 사고과정을 거쳐서 의사결정을 하고, 경험치를 바탕으로 이를 점차 빌드업 시키는 방법이다.

 

 "제한된 합리성’이란 다양한 의사결정 상황에서 인간의 인지적인 한계로 인해 발생하는

의사결정 문제를 인지적 한계 안에서 다룰 수 있는 범위로 축소시키고,

간단해진 과업의 수행에 한해 규범적 규칙을 이용한다는 것을 의미한다."

- 위키백과 -

 

본문에서 강조하고자 했던 내용들을 총정리하는 목적으로 작성해보고자 한다.

 

## 주석(Comment)

1. 부적절한정보

주석의 역할은 코드와 설계에 있어 기술적인 설명을 부연하는 수단이다.

소스 코드 관리 시스템, 버그 추적 시스템, 이슈 추적 시스템, 기타 기록 관리 시스템에 저장할 정보는 주석

변경이력은 [작성자, 수정일, 이슈 번호] 이정도가 적당하겠다.

 

2. 쓸모없는 주석

일단 삭제하자. 오래될수록 레거시가 된다.

 

3. 중복된 주석

코드만으로 충분한데 주석으로 구구절절 설명한다.

코드만으로 충분하지 않다면, 주석을 쓸게 아니라 코드를 수정하자

소스 코드를 더 방대하게 만들고 지저분하게 만들 뿐이다. 

+) 함수 서명만 기술하는 Javadoc도 지양할 것 🙅

 

4. 성의없는 주석

간단하고 명료하게 설명한다.

 

5. 주석 처리된 코드

현업에서 정말 많이 볼 수 있다. 누군가 사용하겠지? 라고 생각하며 아무도 쉽게 건들지 않는다.

이럴 땐 과감히 지워버리자. 만약 필요하게 된다면? 소스 코드를 관리하는 시스템이 기억할 것이다.

필요할 때 복원시키면 된다.

 

## 환경

1. 한 명령어로 전체 시스템을 빌드시킨다. 

빌드시 온갖 파일들을 찾느라 뒤적거리면

불필요한 스크립트를 실행시켜서 각 요소를 따로 빌드시키지 않도록 유의한다.

 

2. 모든 테스트를 한 번에 수행하자

 

##함수

1. 너무 많은 인수

인수가 적을 수록 좋다.

넷 이상은 흠... 고민해보자..

(이 문구를 보자마자, 넷 이상인 인수가 있는지 살펴보러갔다... 😮‍💨 휴.. 다행히 3개까지만 있긴 한데... 찝찝하긴 하다)

 

2. 출력 인수

객체 지향형 프로그래밍에서는 출력 인수를 사용할 일이 거의 없다.

함수 내부의 상태 변경이 필요하다면 함수가 속한 객체의 상태를 변경한다.

as-is) appendFooter(s)

to-be) report.appendFooter()

 

3. 플래그 인수

함수로 boolean값을 넘기지 말자

이는 함수가 여러 기능을 수행한다는 의미이다.

 

4. 죽은 함수

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

 

## 일반

1. 한 소스파일에 여러 언어를 사용하지 않는다.

 

2. 함수나 클래스는 다른 개발자가 당연하게 여길만 동작으로 구현되어야 한다.

 

3. 모든 경계 조건을 찾아내고, 테스트한다.

 

4. 안전 절차를 무시하지 않는다.

    - 컴파일러 경고를 꺼버린다거나, 실패하는 테스트 케이스를 무시하면 나중에 큰 코 다친다.

 

5. 중복

- "한 번, 단 한번 만"

- 중복을 발견할 때마다 추상화의 기회라고 생각하자 (하위 클래스 혹은 루틴으로 분리하기)

- 중복의 유형

(1) 똑같은 코드가 여러 차례 나온다.

(2) 여러 모듈에서 switch/case문이나 if/else문으로 똑같은 조건을 거듭 확인한다. -> 다형성으로 해결

(3) 알고리즘이 유사하나 코드가 서로 다른 중복 -> TEMPLATE METHOD / STRATEGY 패턴으로 해결

 

6. 추상화 수준(레벨)을 맞춰라

- 모든 저차원 개념 : 파생 클래스!

- 모든 고차원 개념 : 기초 클래스!

 

7. 기초 클래스와 파생 클래스는 서로 모르는 사이가 되어야 한다.

기초/파생 즉 고차원, 저차원으로 분리하는 이유는 독립성을 보장하기위해서이다.

독립적이어야 개별 단위로 개발 및 시스템 배치가 가능해진다. 

일반적으로 고차원 기초 클래스 개념을 저차원 파생 클래스 개념으로 분리한다.

 

8. 과도한 정보 

 잘 정의된 인퍼테이스는 많은 함수를 제공하지 않는다.

자료를 숨기자 (캡슐화)

  • 클래스가 제공하는 메서드는 적을 수록 좋다.
  • 클래스에 들어있는 인스턴스 변수 수도 적을 수록 좋다.
  • 함수가 아는 변수의 수도 적을 수록 좋다.

 

9. 죽은 코드

  • 불가능한 조건을 확인하는 if문
  • throw 문이 없는 try~ catch문에서의 catch 블록
  • 아무도 호출하지 않는 유틸함수
  • swtich/case 문에서 불가능한 case 조건

등은 모두 죽은 코드이다. 깔끔하게 삭제하자

 

10. 수직 분리

변수와 함수는 사용되는 위치에 수직으로 가깝게 배치한다.

지역변수 : 사용하기 직전에 선언하기

비공개 함수 : 처음 호출한 직후에 정의하기

 

11. 일관성 부족

어떤 개념을 특정방식으로 구현했다면, 유사한 개념도 유사한 네이밍과 방식으로 구현해야 한다.

 

12. 잡동사니

비어있는 기본 생성자

 

13. 인위적 결합

당장 편하자고 직접적인 상호작용이 없는 곳에 변수,상수,함수를 위치시키는 어리석은 행동을 하지 말자.

변수, 상수, 함수를 선언할 때는 항상 신중하게 올바른 위치를 고민하여 선언하자

 

14. 기능 욕심

클래스의 메서드는 자기 클래스에만 관심을 가져야지 괜히 엉뚱한 곳을 기웃거리면 안된다.

 

15. 선택자 인수

일반적으로 인수를 넘겨 동작을 선택하는 방법보다는 새로운 함수를 만드는 것이 좋다.

 

18. 부적절한 static 함수

"static"은 정적, 말그대로 '변하지 않는'. 즉, 정해져 있다는 뜻이다.

따라서 static 함수로 정의할 때는 재정의할 가능성이 없는지 꼼꼼히 따져봐야 하며, 조금이라도 의문점이 생긴다면 instance로 정의하는 것이 좋다.

 

21. 알고리즘을 이해해라.

코드가 돌아가는 것을 이해하는 것과 이를 돌아가기 위한 알고리즘이 올바르다는 사실을 이해하는 것은 다르다.

알고리즘을 명확하게 이해해야, 구현에 확신을 가질 수 있다.

 

23. If/Else문 또는 Switch/Case 문보다 다형성을 사용하자

*다형성 객체를 이용해 switch 문을 대신해라.

(* 다형성 : 하나의 객체가 여러 가지 타입을 가질 수 있는 것으로

부모 클래스 타입의 참조 변수로 자식 클래스 타입의 인스턴스를 참조한다.)

 

25. 의미가 분명하지 않는 토큰들은 상수로 교체한다

//as-is
assertEquals(7777, Employee.find("Jonh Doe").employeeNumber();

//to-be
assertEquals(HOURLY_EMPLOYEE_ID, Employee.find(HOURLY_EMPLOYEE_NAME).employeeNumber();