클린코드 17장 (끝)

jiwon·2022년 5월 9일
0

클린코드

목록 보기
17/17
post-thumbnail

17장 냄새와 휴리스틱

🔎주석

부적절한 정보

이슈 추적, 소스코드 관리 등 다른 시스템에 저장할 정보는 주석으로 적절하지 않다. 이러한 내용은 괜히 소스 코드만 번잡하게 만든다.

쓸모 없는 주석

주석은 빨리 낡는다. 추후 쓸모 없어질 주석은 아예 달지 않는 편이 좋고, 쓸모 없어진 주석은 빨리 삭제해야 한다.

중복된 주석

코드만으로 충분한데 구구절절 설명하는 주석이 중복된 주석이다. 이런 주석은 필요가 없다.

주석 처리된 코드

이런 주석이 나오면 신경이 아주 거슬린다. 누군가에게 필요한 코드라고 생각해서 아무도 삭제하지 않고, 주석코드는 그 자리에서 하루하루 낡아간다...흉물 그 자체다! 얼른 지워버리자.(아무리 그래도 흉물이라니..이 책 넘 막말심함ㅠㅠ)

🔎환경

여러 단계의 빌드

필요한 파일을 찾느라 여기저기 뒤적일 필요가 없어야 한다. 한 명령으로 전체를 체크아웃해서 한 명령으로 빌드할 수 있어야 한다.

여러 단계의 테스트

모든 단위 테스트는 한 명령으로 돌려야 한다. IDE에서 버튼 하나로 모든 테스트를 돌린다면 가장 이상적이다.

🔎함수

너무 많은 인수

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

출력 인수

출력 인수는 직관을 정면으로 해친다. 쓰지 말자.

※출력 인수란..?
내가 코테볼때마다 배열 값 변경 등에 매우매우 많이 썼다고 볼 수 있을 것 같은데...쉽게 말하면 변수 등을 인자로 받은 다음에 그 변수의 값을 함수에서 변경하는 것이다.

플래그 인수

플래그 인수는 혼란을 초래하므로 피해야 마땅하다.
얘도 코테칠때 참 많이 썼는데😂

죽은 함수

아무도 호출하는 함수는 삭제한다. 소스 코드 관리 시스템이 모두 기억하므로 걱정할 필요 없다.

🔎일반

한 소스 파일에 여러 언어를 사용한다

소스 파일 하나에 언어 하나만 사용하는 방식이 가장 좋다. 현실적으로는 여러 언어를 써야만 하는 일이 있지만...그래도 최대한 줄이도록 애써야 한다.

당연한 동작을 구현하지 않는다.

당연한 동작을 구현하지 않으면 코드를 읽거나 사용하는 ㅏ람이 더 이상 함수 이름만으로 기능을 직관적으로 예상하기 어렵다. 최소 놀람의 원칙에 의거하여 함수나 클래스는 다른 프로그래머가 당연하게 여길 동작과 기능을 제공해야 한다.

안전 절차 무시

안전 절차를 무시하면 위험하다. 예를 들어 컴파일러 경고 일부를 꺼버리면 빌드가 쉬워질지 모르지만 자칫하면 끝없는 디버깅에 시달릴 수 있다.

중복

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

추상화 수준이 올바르지 못하다.

추상화로 개념을 분리할 때는 철저해야 한다. 모든 저차원 개념은 파생 클래스에 넣고, 모든 고차원 개념은 기초 클래스에 넣는다. 세부 구현과 관련한 변수, 함수 등은 기초 클래스에 넣으면 안된다. 기초 클래스는 구현 정보에 무지해야 한다.

기초 클래스가 파생 클래스에 의존한다.

개념을 기초 클래스와 파생 클래스로 나누는 가장 흔한 이유는 두 개념을 분리해 독립성을 보장하기 위해서이다. 그러므로 기초 클래스가 파생 클래스를 사용한다면 뭔가 문제가 있다는 말이다. 기초 클래스는 파생 클래스를 아예 모르는 것이 바람직하다.

과도한 정보

인터페이스를 매우 작게 그리고 매우 깐깐하게 만들어라. 정보를 제한해 결합도를 낮춰라.

죽은 코드

죽은 코드란 실행되지 않는 코드를 가리킨다. 죽은 코드는 설계가 변해도 제대로 수정되지 않고 갈수록 악취를 풍긴다. 빨리 제거해주자.

수직 분리

변수와 함수는 사용되는 위치에 가깝게 정의한다. 지역 변수는 처음으로 사용되기 직전에 선언하며 수직으로 가까운 곳에 위치해야 한다. 선언한 위치로부터 몇백 줄 아래에서 사용하면 안된다. 비공개 함수 또한 호출하는 위치와 정의하는 위치를 가깝게 해야한다.

일관성 부족

어떤 개념을 특정 방식으로 구현했다면 유사한 개념도 같은 방식으로 구현한다. 표기법은 신중하게 선택하며, 일단 선택한 표기법은 신중하게 따른다.

잡동사니

아무도 사용하지 않는 변수, 함수, 쓰레기 주석들은 코드만 복잡하게 만든다. 이러한 잡동사니는 깔끔하게 정리하자.

인위적 결합

서로 무관한 개념을 인위적으로 결합하지 않는다. 예를 들어 일반적인 enum은 특정 클래스에 속할 이유가 없다. 함수, 상수, 변수를 선언할 때는 시간을 들여 올바른 위치를 고민한다. 그저 당장 편한 곳에 선언하고 내버려두면 안된다.

기능 욕심

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

선택자 인수

함수명(false) 이런식으로 해서 인자값이 false냐 true냐에 따라 다르게 동작하게 하는거...별로라고 한다. 이렇게 구현하고 중복 없앴다구 좋아한적 많은디...( ._.) 암튼 이렇게 하지 말고 함수 여러개로 쪼개는 것이 바람직하다. 함수를 잘게 조사버리면 따로 구현한다고 해도 중복이 없을 것이다.

모호한 의도

코드를 짤 때는 의도를 최대한 분명히 밝힌다.

잘못 지운 책임

코드 설계 시 코드 배치 위치를 결정하는 것은 중요하다.코드는 독자가 자연스럽게 기대할 위치에 배치한다. 때때로 개발자에게 편한 위치에 배치하기도 한다.

부적절한 static 함수

static 함수란? 재정의 불가능한 함수.
그런데 우리는 간혹 static으로 정의하면 안 되는 함수를 static으로 정의한다. static 함수를 정의할 때는 재정의할 가능성은 없는지 꼼꼼히 따져봐야 한다.

서술적 변수

서술적인 변수 이름은 썩 괜찮다. 좋은 변수 이름은 어렵던 모듈을 읽기 쉽게 만들어 준다.

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

이름에 기능이 드러나도록 적절히 이름을 지어야 한다.

알고리즘을 이해해라

대다수 괴상한 코드는 사람들이 알고리즘을 충분히 이해하지 않은채 여기저기 if문과 플래그문을 난사해 코드를 돌린 탓이다. 구현이 끝났다고 선언하기 전에 함수가 돌아가는 방식을 확실히 이해하는지 확인하라.

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

한 모듈이 다른 모듈에 의존한다면 물리적인 의존성도 있어야 한다.의존하는 모든 정보를 명시적으로 요청하는 편이 좋다.

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

대다수 개발자가 switch 문을 사용하는 이유는 올바르기보다는 손쉬운 선택이기 때문이다. 따라서 그 이전에 다형성을 먼저 고려하라는 의미다.

표준 표기법을 따르라

팀은 업계 표준에 기반한 구현 표준을 따라야 하고, 팀이 정한 표준은 팀원들 모두가 따라야 한다.

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

일반적으로 코드에서 숫자를 사용하지 말라는 규칙이다. 1,2,3같은 애들은 괜찮다. 하지만 3.141592...보다는 Math.PI가 낫다.

정확하라

코드에서 뭔가를 결정할 때는 정확히 결정한다. 대충 결정해서는 안된다. 예를 들어 함수가 null을 반환할지도 모른다면 반드시 점검해야한다.

관례보다 구조를 사용하라

설계 결정을 강제할 때는 규칙보다 관례를 사용한다. 명명 관례도 좋지만 구조 자체로 강제하는게 더 좋다.

조건을 캡슐화하라

if 문 안에 구구절절 넣지 말고 조건의 의도를 분명히 밝히는 함수로 표현하라.

부정 조건은 피하라

부정 조건은 긍정 조건보다 이해하기 어렵다. 가능하면 긍정 조건으로 표현한다.

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

함수는 한 가지만 수행해야 한다. 만약 함수가 여러가지 일을 한다면, 한 가지만 수행하는 좀 더 작은 함수 여럿으로 나눠야 마땅하다.

숨겨진 시간적인 결합

어떤 경우에는 함수들이 실행되는 순서가 중요하다. 이런 경우 함수 인수를 적절히 배치해 함수가 호출되는 순서를 명백히 드러낼 수 있도록 하자.

일관성을 유지하라

코드 구조를 잡을 때는 이유를 고민하라. 구조에 일관성이 없어 보인다면 남들이 맘대로 바꿔도 괜찮다고 생각한다. 시스템 전반에 걸쳐 구조가 일관성이 있다면 남들도 일관성을 따르고 보존한다.

경계 조건을 캡슐화하라.

경계 조건은 코드 여기저기에서 처리하지 말고 한 곳에서 별도로 처리한다. 다시 말해, 코드 여기저기에 +1이나 -1을 흩어놓지 않는다.

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

함수 내 모든 문장은 추상화 수준이 동일해야 한다. 그리고 그 추상화 수준은 함수 이름이 의미하는 작업보다 한 단계만 낮아야 한다. 추상화 수준이 막 뒤섞여 있다면 차근차근 분리해주자.

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

추상화 최상위 단계에 둬야 할 기본값 상수나 설정 관련 상수를 저차원 함수에 숨겨서는 안 된다. 대신 고차원 함수에서 저차원 함수를 호출할 때 인수로 넘긴다.

추이적 탐색을 피하라

일반적으로 한 모듈은 주변 모듈을 모를수록 좋다.내가 아는 모듈이 연이어 자신이 아는 모듈을 따라가며 시스템 전체를 휘저을 필요가 없다. 자신이 직접 사용하는 모듈만 알면 충분하다.

🔎자바

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

패키지에서 클래스를 둘 이상 사용한다면 와일드 카드를 사용해 패키지 전체를 가져오라.

import package.*;

당연한 소리같지만 경고메세지를 더블클릭해가며 임포트하다보면 쉽게 어기곤 하는 규칙이다...ㅋㅋㅋ

상수는 상속하지 않는다.

어떤 프로그래머는 상수를 인터페이스에 넣은 다음 그 인터페이스를 상속해 해당 상수를 사용한다. 별로다... 차라리 static import를 사용하라.

※static import?
일반적인 import와는 다르게 메소드나 변수를 패캐지, 클래스명없이 접근가능하게 해준다.

상수 대 Enum

자바5는 enum을 제공한다. 마음껏 활용하라! public static final int라는 옛날 기교를 더 이상 사용할 필요가 없다.

🔎이름

서술적인 이름을 사용하라

이름은 성급하게 정하면 안된다. 서술적인 이름을 신중하게 골라라. 대충 정하기에 이름은 너무나도 중요하다..

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

구현을 드러내는 이름은 피하라. 작업 대상 클래스나 함수가 위치하는 추상화 수준을 반영하는 이름을 선택하라.

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

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

명확한 이름

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

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

이름 길이는 범위 길이에 비례해야 한다. 범위가 5줄 안팍이라면 int i 같은 애들도 괜찮다. 오히려 이런애들이 긴 이름을 가지면 헷갈리기만 한다. 반대로 범위가 길어지면 긴 이름을 사용해야 한다.

인코딩을 피해라

인코딩=암호화
이름에 유형 정보나 범위 정보를 넣어서는 안된다. 오늘날 개발 환경에서는 이름 앞에 m_ 이나 f와 같은 접두어가 불필요하다.요즘은 세상이 좋아져서 IDE가 색깔 등으로 구분해준다.

저런 접두어를 붙여본적이 없는데...색깔이 없는 자바라니 대체 얼마나 옛날인거지!?

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

이름에 부수 효과를 숨기지 않는다. 실제로 여러 작업을 수행하는 함수에다 동사 하나만 달랑 사용하면 곤란하다.

🔎테스트

불충분한 테스트

테스트는 몇 개나 만들어야 충분할까? 불행히도 많은 프로그래머들이 이 정도면 충분하지 않을까 척도를 사용한다. 테스트 케이스는 잠재적으로 깨질 만한 부분을 모두 테스트해야 한다.

커버리지 도구를 사용하라

커버리지 도구는 테스트가 빠뜨리는 공백을 알려준다. 예를 들어 절대 실행되지 않는 if 블럭 같은 애들을 색깔로 표시해준다.

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

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

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

때로는 요구사항이 불분명하기에 프로그램이 돌아가는 방식을 확신하기 어렵다. 불분명한 요구사항은 테스트 케이스를 주석으로 처리하거나 테스트 케이스에 @Ignore를 붙여 표현한다. 불분명한 요구사항을 판별하는 기준은 테스트 케이스의 컴파일 여부에 달려있다.

경계 조건을 테스트하라

경계 조건은 각별히 신경 써서 테스트한다. 알고리즘의 중앙 조건은 올바로 짜 놓고 경계 조건에서 실수하는 경우가 흔하다.

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

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

실패 패턴을 살펴라

때로는 테스트 케이스가 실패하는 패턴으로 문제를 진단할 수 있다. 테스트 케이스를 최대한 꼼꼼히 짜라는 이유도 여기에 있다.

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

통과하는 테세트가 실행하는 코드들, 혹은 실행하지 않는 코드들을 살펴보면 실패하는 테스트 케이스의 실패 원인이 드러난다. 이 코드를 실행하면 실패하는군...을 알 수 있게 된다는 뜻.

테스트는 빨라야 한다.

느린 테스트 케이스는 실행하지 않게 된다. 일정이 촉박하면 얘네들을 막 건너뛸 확률이 높다. 그러므로 테스트케이스가 빨리 돌아가게 최대한 노력한다.

profile
개발 공부합니다. 파이팅!

0개의 댓글