대범하게

[클린코드] 40~43일차 - 냄새와 휴리스틱 (주석/환경/함수/일반) 본문

알아두면쓸데있는신기한잡학사전/독서일지

[클린코드] 40~43일차 - 냄새와 휴리스틱 (주석/환경/함수/일반)

대범하게 2023. 12. 18. 22:30
반응형

[클린코드] 40일차 - 냄새와 휴리스틱

클린코드 40일차 (p. 370 ~ 378 (17장) )

클린코드 41일차 (p. 379 ~ 387 (17)

클린코드 42일차 (p. 388 ~ 395 (17)

클린코드 43일차 (p. 396 ~ 403 (17)

17장 냄새와 휴리스틱

좋지 못한 코드는 냄새가 난다... (ㅋ)(ㅠ)

그리하여 이 장에서는 다양한 코드 냄새와 코드를 짜면서 사용하는 기교, 휴리스틱을 소개한다. 

주석

1. 부적절한 정보

다른 시스템에 저장할 정보는 주석으로 적절하지 못하다.

2. 쓸모 없는 주석

오래된 주석, 엉뚱한 주석, 잘못된 주석은 더 이상 쓸모가 없음

주석은 '낡는다.'

쓸모 없어진 주석은 제발 삭제하자.

3. 중복된 주석

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

4. 성의 없는 주석

주석을 달거면 시간을 들여 멋지게 작성하자.

당연한 소리 반복하지 말고 간결하게 !

5. 주석 처리된 코드

코드를 읽다가 주석으로 처리된 코드가 줄줄이 나오면 신경이 아주 거슬린다. (나다..)

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

 

환경

1. 여러 단계로 빌드하는 건 피하자.

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

2. 여러 단계로 테스트하는 건 피하자. 

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

 

함수

1. 인수가 너무 많으면 좋지 않다. 

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

2. 출력 인수 -> 피하자.

출력 인수는 직관적으로 맞지 않다.

일반적으로 인수는 입력으로 간주한다.

 

함수에서 뭔가의 상태를 변경해야 한다면 함수가 속한 객체의 상태를 변경한다.

3. 플래그 인수는 함수가 여러 기능을 한다는 명백한 증거. -> 피하자.

플래그 인수는 함수가 여러 기능을 수행한다는 증거로 피해야 마땅하다.

4. 죽은 함수 == 사용하지 않는 함수

죽은 코드는 낭비다.

 

일반

1. 한 소스 파일에 여러 언어를 사용하는 것(은 말도 안 된다.)

오늘날 프로그래밍 환경에서는 한 소스 파일 내에 다양한 언어를 사용할 수 있지만,,,,

소스 파일 하나에 언어 하나만 사용하는 방식이 가장 좋다.

2. 당연한 동작을 구현하지 않는다. -> 함수 이름만으로 함수 기능을 구현하자.

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

3. 경계를 올바로 처리하지 않는다. 

모든 경계 조건, 모든 구석진 곳, 모든 기벽, 모든 예외는 우아하고 직관적인 알고리즘을 좌초시킬 암초다.

스스로의 직관에 의존하지 마라.

모든 경계조건을 찾아내고 테스트하는 테스트 케이스를 작성하라.

4. 안전 절차 무시

실패하는 테스트 케이스를 일단 제껴두고 나중으로 미루는 태도는 위험하다.

5. 중복 (이 책에 나오는 가장 중요한 규칙 중 하나)

코드에서 중복을 발견할 때마다 추상화할 기회로 간주하라.

중복된 코드를 하위 루틴이나 다른 클래스로 분리하라.

 

- 똑같은 코드가 여러 차례 나오는 중복은 간단한 함수로 교체한다.

- 여러 모듈에서 switch/case나 if/else 문으로 똑같은 조건을 거듭 확인하는 중복은 다형성으로 대체해야한다.

- 알고리즘이 유사하나 코드가 서로 다른 중복은 TEMPLATE METHOD 패턴이나 STRATEGY 패턴으로 중복을 제거한다. 

6. 추상화 수준이 올바르지 못할 경우

추상화는 저차원 상세 개념에서 고차원 일반 개념을 분리한다.

추상화로 개념을 분리할 때는 철저하게 저차원은 파생 클래스에 넣고 고차원은 기초 클래스에 넣어야 한다.

7. 기초 클래스가 파생 클래스에 의존하는 것

일반적으로 기초 클래스는 파생 클래스를 아예 몰라야 마땅하다.

8. 과도한 정보

잘 정의된 모듈은 인터페이스가 아주 작다.  -> 반드시 호출해야하는 온갖 함수들이 있다. -> 결합도가 높다.

부실하게 정의된 모듈은 인터페이스가 구질구질 하다. -> 많은 함수를 제공하지 않는다. -> 결합도가 낮다.

 

클래스가 제공하는 메서드 수는 작을수록 좋다. 함수가 아는 변수 수도 작을수록 좋다.

자료를 숨겨라. 유틸리티 함수를 숨겨라. 상수와 임시 변수를 숨겨라. 메서드나 인스턴스 변수가 넘쳐나는 클래스는 피하라. 하위 클래스에서 필요하다는 이유로 protected 변수나 함수를 마구 생성하지 마라. 인터페이스를 매우 작게 그리고 매우 깐깐하게 만들어라. 정보를 제한해 결합도를 낮춰라. 

9. 죽은 코드

죽은 코드 ==실행되지 않은 코드 (ex: 불가능한 조건을 확인하는 if문, throw문이 없는 try문에서 catch 블록)
죽은 코드는 시간이 지나면 악취가 난다..  적절한 장례식을 치뤄주라. like 시스템에서 제거

10. 수직 분리

변수와 함수는 사용되는 위치에 가깝게 정의한다.

비공개 함수는 처음으로 호출한 직후에 정의한다.

비공개 함수는 전체 클래스 범위에 속하지만, 그래도 정의하는 위치와 호출하는 위치를 가깝게 유지한다.

11. 일관성 부족

표기법은 신중하게 선택하며 일단 선택한 표기법을 신중히 따르자.

간단한 일관성만으로도 코드를 읽고 수정하기 쉬워진다.

12. 잡동사니

비어있는 기본 생성자가 왜 필요한가? 지워라.

소스파일은 언제나 깔끔하게 정리하라. 

13. 인위적 결합

일반적으로 인위적인 결합은 직접인 상호작용이 없는 두 모듈 사이에 일어난다.

목적 없이 변수, 상수, 함수를 당장 편한 위치에 넣어버린 결과이다.

변수, 상수, 함수를 선언할 때에는 시간을 들여 올바른 위치를 고민해야 한다.

14. 기능 욕심

클래스 메서드는 자기 클래스의 변수와 함수에 관심을 가져야지 다른 클래스의 변수와 함수에 관심을 가져서는 안된다.

메서드가 다른 객체의 참조자와 변경자를 사용해 그 객체 내용을 조작한다면 메서드가 그 객체 클래스의 범위를 욕심내는 탓이다.

15. 선택자 인수

- 선택자 인수 : 함수나 메서드의 인수 중 하나가 다른 인수들의 처리 방식을 결정하는 역할을 하는 것.

일반적으로, 인수를 넘겨 동작을 선택하는 대신 새로운 함수를 만드는 편이 좋다. 

16. 모호한 의도

코드를 짤 때는 의도를 분명히 밝혀라.

*의도가 분명하지 않은 방법 = 행을 바꾸지 않고 표현한 수식 / 헝가리식 표기법, 매직 번호 

17. 잘못 지운 책임

최소 놀람의 원칙 적용 == 독자가 자연스럽게 기대할 위치에 배치한다. 

18. 부적절한 static 함수

함수를 재정의할 가능성이 있으면 static 함수로 정의하면 안 된다. 
일반적으로 static 함수보다 인스턴스 함수가 더 좋으므로, 조금이라도 의심스럽다면 인스턴스 함수로 정의한다.

19. 서술적 변수

프로그램 가독성을 높이는 가장 효과적인 방법 중 하나는

계산을 여러 단계로 나누고 중간 값으로 서술적인 변수 이름을 사용하는방법이다. 
서술적인 변수 이름은 많을수록 좋으며, 가독성을 크게 증가시킨다.

20. 이름과 기능이 일치하는 함수

이름만으로 분명하지 않기에 구현을 살피거나 문서를 뒤적여야 한다면
더 좋은 이름으로 바꾸거나 더 좋은 이름을 붙이기 쉽도록 기능을 정리해야 한다.

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

알고리즘을 안다고 생각하지만 실제는 코드가 '돌아갈'때까지 굴려본다. 

'돌아간다'는 사실은 어떻게 아느냐고? == > 테스트 케이스를 모두 통과하니까!

 

구현이 끝났다고 선언하기 전에 함수가 돌아가는 방식을 확실히 이해하는지 확인하라. 

 

알고리즘이 올바르다는 사실을 이해하려면 기능이 뻔히 보일 정도로 함수를 깔끔하게 재구성하는 방법이 최고다.

22. 논리적 의존성은 물리적으로 드러내라.

한 모듈이 다른 모듈에 의존한다면 물리적인 의존성도 있어야 한다.

즉, 의존하는 모든 정보를 명식적으로 요청하는 편이 좋다. 

23. If/Else 혹은 Switch/Case 문보다 다형성을 사용하라

switch를 선택하기 전에 다형성을 먼저 고려하라.

24. 표준 표기법을 따르라

팀은 업계 표준에 기반한 구현 표준을 따르도록 한다.

표준을 설명하는 문서는 코드 자체로 충분해야 하며 별도 문서를 만들 필요는 없어야 한다.
팀이 정한 표준을 팀원들 모두가 따라야 한다. 

25. 매직 숫자는 명명된 상수로 교체하라

소프트웨어 개발에서 가장 오래된 규칙 중 하나이다. ==> 일반적으로 코드에서 숫자를 사용하지 말라.

 

ex) 86,400이라는 숫자는 SECONDS_PER_DAY 상수 뒤로 숨긴다.
쪽 당 55줄을 인쇄한다면 숫자 55는 LINES_PER_PAGE 상수 뒤로 숨긴다.


'매직 숫자’라는 용어는 숫자만 의미하지 않는다. 

의미가 분명하지 않은 토큰을 모두 가리킨다.

26. 정확하라.

코드에서 모호성과 부정확은 의견차나 게으름의 결과다. 

27. 관례보다 구조를 사용하라

설계 설정을 강제할 때는 규칙보다 관례를 사용한다.

명명 관례도 좋지만 구조 자체로 강제하면 더 좋다.

switch/case 문을 매번 똑같이 구현하게 강제하기는 어렵지만, 파생 클래스는 추상 클래스를 모두 구현하지 않으면 안된다.

28. 조건을 캡슐화하라

boolean 논리는 이해하기 어렵다.

조건의 의도를 분명히 밝히는 함수로 표현하라. -> 즉, boolean 조건을 함수로 만들라!

29. 부정 조건은 피하라

부정 조건은 긍정 조건보다 이해하기 어렵다.

가능하면 긍정 조건으로 표현한다.

30. 함수는 한 가지만 해야 한다.

한 함수 안에 여러 단락을 이어 만들지 말고,

한 가지만 수행하는 좀 더 작은 함수를 여럿으로 나눠야 마땅하다.

31. 숨겨진 시간적인 결합

때로는 시간적인 결합이 필요하다. 하지만 숨겨서는 안된다.

함수를 짤때는 함수 인수를 적절히 배치해, 함수가 호출되는 순서를 명백히 드러낸다.

32. 일관성을 유지하라

코드 구조를 잡을 대는 이유를 고민하라.

그리고 그 이유를 코드 구조로 명백히 표현하라.

코드 구조에 일관성이 없다면 남들이 맘대로 바꿔도 된다고 생각한다.

33. 경계 조건을 캡슐화하라

경계 조건은 변수로 캡슐화하는 것이 좋다.

34. 함수는 추상화 수준을 한 단계만 내려가야 한다.

함수 내 모든 문장은 추상화 수준이 동일해야 한다.

그리고 그 추상화 수준은 함수 이름이 의미하는 작업보다 한 단계만 낮아야 한다.

 

이 장에서 설명하는 휴리스틱 중 가장 이해하기 어렵다. 

35. 설정 정보는 최상위 단계에 둬라

추상화 최상위 단계에 둬야 할 기본값 상수나 설정 관련 상수를 저차원 함수에 숨겨선 안된다.

대신 고차원 함수에서 저차원 함수를 호출할 때 인수로 넘겨야 한다.

36. 추이적 탐색을 피하라

일반적으로 한 모듈은 주병 모듈을 모를수록 좋다.

자신이 직접 사용하는 모듈만 아는 것이 좋다.

내가 아는 모듈이 연이어 자신이 아는 모듈을 따라가며 시스템 전체를 휘저을 필요가 없다는 의미이다. 

자바

1. 긴 import 목록을 피하고 와일드카드를 사용하라.

긴 import 목록은 읽기 부담스럽다.

사용하는 패키지를 간단히 명시하면 충분하다.

2. 상수는 상속하지 않는다.

상속하지 말고 대신 static import를 사용하라.

3. 상수 vs Enum

enum을 마음껏 활용하라!

 

이름

1. 서술적인 이름을 사용하라.

소프트웨어의 가독성의 90%는 이름이 결정한다.

2. 적절한 추상화 수준에서 이름을 선택하라.

구현을 두러내는 이름은 피하라.

작업 대상 클래스나 함수가 위치하는 추상화 수준을 반영하는 이름을 선택하라.

3. 가능하다면 표준 명명법을 사용하라.

프로젝트에 프로젝트에 유효한 의미가 담긴 이름을 많이 사용할수록 독자가 코드를 이해하기 쉬워진다.

4. 명확한 이름을 사용하라.

함수나 변수의 목적을 명확히 밝히는 이름을 선택한다.

5. 긴 범위는 긴 이름을 사용하라.

이름의 길이는 범위의 길이에 비례해야 한다.

범위가 작으면 아주 짧은 이름을 사용해도 괜찮다.

하지만 범위가 길어지면 긴 이름을 사용한다.

6. 인코딩을 피하라.

이름에 유형정보나 범위 정보를 넣어서는 안된다.

7. 이름으로 부수 효과를 설명하라.

함수, 변수, 클래스가 하는 일을 모두 기술하는 이름을 사용하라.

여러 작업을 수행하는 함수에다 동사 하나만 사용하면 곤란하다.

 

테스트

1. 불충분한 테스트

테스트 케이스는 잠재적으로 깨질 만한 부분을 모두 테스트해야 한다.

테스트 케이스가 확인하지 않는 조건이나 검증하지 않는 계산이 있다면 그 테스트는 불완전하다.

2. 커버리지 도구를 사용하라!

커버리지 도구는 테스트가 빠뜨리는 공백을 알려준다.

3. 사소한 테스트를 건너뛰지 마라.

사소한 테스트는 짜기 쉽다. 사소한 테스트가 제공하는 문서적 가치는 구현에 드는 비용을 넘어선다.

4. 무시한 테스트는 모호함을 뜻한다.

때로는 요구사항이 불분명하기에 프로그램이 돌아가는 방식을 확신하기 어렵다. 

불분명한 요구사항은 테스트 케이스에 @Ignore 를 붙여 표현하라.

선택 기준은 모호함이 존재하는 테스트 케이스가 컴파일이 가능 / 불가능에 달려있다.

5. 경계 조건을 테스트하라.

경계 조건은 각별히 신경써서 테스트한다.

6. 버그 주변은 철저히 테스트하라.

한 함수에서 버그를 발견했다면 그 함수를 철저히 테스트하는 편이 좋다.

7. 실패 패턴을 살펴라.

8. 테스트 커버리지 패턴을 살펴라.

통과하는 테스트가 실행하거나 실행하지 않는 코드를 살펴보라. 실패 원인이 드러난다.

7. 테스트는 빨라야한다.

느린 테스트 케이스는 실행하지 않게 된다.

Comments