클린 코드 3장 함수에 대한 인용과 부가 설명 요약 그리고 내가 한 잘못들에 대해서 서술해보았다.
-는 저자의 생각을 요약한 것.
*는 내 경험에 의한 부가설명 및 반성이다.
본문
작게 만들어라
- 저자의 경험 상, 함수는 작을수록 좋다. 어느정도 작냐면 20줄보다도 작은 게 좋다한다.
들여쓰기는 1단,2단까지만 : 더 들어가면 가독성이 나빠진다.
* if문 두 개 겹쳐있으면, 진짜 읽고싶지 않다. 매우 동의한다.
함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야 한다.
- 함수 내에 또 다른 함수를 뽑아서 이름을 붙일 수 있다면, 한 가지만 하는 것이 아니다
- 함수 내 추상화 수준이 동일해야한다 (아래 부가 설명)
함수 내 추상화 수준이 동일해야한다.
- 추상화 수준이 다른 함수가 섞여있으면 문맥이 헷갈린다.
- 이야기 읽듯이 내려가면서 읽을 수 있게 만드는 것이 가장 좋다.
* 가끔 큰 개념과 작은 개념이 섞이면 코드 읽으면서 흐름이 꼬일 때가 있긴하다.
서술적인 이름을 사용해라
- 길고 확실한 이름을 사용해라 (불명확한 이름, 긴 주석보다 이게 더 낫다.)
- 일관성 있는 이름을 사용해라 : 모듈 내 같은 문구, 명사, 동사를 사용해라
* 솔직히 아직 긴 이름이 꼴보기 싫어서 고민중이다. 다만, 예전에 내 코드 유지보수를 할 때, 함수명이 getInfo()였던 적이 있는데 꽤 불편했던 게 생각나서 반성된다.
함수 인수는 최대한 적게 써라
- 0개가 이상적이다. 3개 이상은 피해라
- 테스트할 때, 인수의 개수에 비례하여 테스트가 복잡해진다. 적을수록 좋다.
- 출력 인수 사용은 지양하자 (아래 상세 내용)
- 플래그 인수 사용은 지양하자 : 쓸데없이 인수를 많아지게 하는 요소다. 또한, 플래그 인수가 있다는 것은 그 함수가 여러가지를 처리하는 함수로 하나가 아닌 여러가지를 하는 함수가 된다. (true/false에 따라서 다른 동작을 하는 함수니까)
- 인수 객체 사용 시, 인수 개수를 주의하자 : 인수를 줄이는 척할 뿐 결국 전체 인수 양은 똑같다. 총 몇 가지 인수를 사용하는 지 주의하자.
- 동사와 키워드를 함께 표현하자.
assertEquals(expected : number, actual: number) : bad
assertExpectedEqualsActual(expected : number, actual: number) : good
* 동사와 키워드 함께 표현하는 부분 너무 좋은 거 같다. 예전에 expected와 actual 바꿔 써서 에러난 적이 있는데, 함수명에서 확인 가능하니 실수가 없을 거 같다.
출력 인수 사용은 지양하자
- 출력 인수 : 입력 인수를 받아서 내부 값을 수정한 뒤 그 인수를 반환하는 것
- 객체 지향 전에서는 이 부분이 필요했지만, 지금은 필요없다. 이를 위해서 this가 존재하기 때문이다. 필요하다면, 함수가 속한 객체의 상태를 변경해라
* 출력 인수라는 것도 사실 여기서 처음봤고, 인터넷 검색결과이다. 문맥상 맞는 표현이여서 가져왔다.
* 아직 써본 적이 없고, 앞으로도 안 쓸 거 같다.
부수 효과를 일으키지 마라. (함수에서 한 가지만 해라!!)
public class UserValidator {
private Cryptographer cryptographer;
public boolean checkPassword(String userName, String password) {
User user = UserGateway.findByName(userName);
if (user != User.NULL) {
String codedPhrase = user.getPhraseEncodedByPassword();
String phrase = cryptographer.decrypt(codedPhrase, password);
if ("Valid Password".equals(phrase)) {
Session.initialize();
return true;
}
}
return false;
}
}
- 부수 효과는 거짓말이다. 다 하나의 함수에서 여러가지를 해서 생기는 일이다.
- 위 코드는, password를 check한다하지만, Session.initialize()를 한다. 여기서, 문제가 생길 것이다. checkPassword는 세션을 초기화해도 괜찮은 상황에서만 사용가능한 함수가 된다. 그렇지 않을 때 사용하면, 부수 효과를 만들어낸다.
- 만약, 이렇게 써야할 경우 checkPasswordAndIntializeSession()으로 작성해야한다. (물론 이 경우 두 가지를 하긴 하지만)
* 굉장히 찔린다. 매 번 이렇게 짰는데 굉장히 무섭다. 반성하는 중이다.
명령과 조회를 분리해라
- 뭔가를 수행하거나, 뭔가를 답하거나 둘 중 하나만 해야한다. 둘 다 하면 혼란을 야기한다. (객체를 바꾸거나, 조회하거나 둘 중 하나만 해라)
* 이 부분도 어쩌다보니 get/set을 나누어서 사용중인데 주의하도록 해야겠다.
오류코드보다 예외를 사용해라
- 오류 코드를 사용하면, 오류 코드에 대한 대응을 해야한다. 하지만, 예외를 사용하면 오류 코드를 본래 코드에서 구분할 수 있어서 깔끔해진다.
try/catch 블록 뽑아내기
- try/catch도 마찬가지다. 별도의 함수로 뽑아내는 편이 좋다.
- 오류 처리도 한 가지 작업이다. 오류를 처리하는 함수는 오류만 처리해야한다.
* 이거는 좀 고민된다. 너무 함수가 길어지고 많아지지 않나 싶다. 좀 더 경험이 필요할 수도 있다.
반복하지 마라
- 중복을 제거하는 것이 중요하다. 중복이 4곳 있다면 4배의 수정이 필요하다. 중복은 모든 악의 근원이다.
* 이 또한 찔리는 부분이다. 뭔가 애매하게 달라서 다른 함수를 만들고 그 코드가 변해서 4군데씩 수정하는 건 지금 일상이다... 고쳐야겠다.
구조적 프로그래밍
- 데이스크트라는 입구(input)와 출구(return문)이 각각 하나여야한다 주장한다.
* 구조적 프로그래밍 => 절차적 프로그래밍(=== 명령형 프로그래밍) => 함수형 프로그래밍 / 객체지향 프로그래밍 식으로 영향을 주었다 한다.
결론
함수는 글과 비슷하다. 처음에는 즉흥적으로 짜면서 단위테스트를 같이 만든다. 그 후, 코드를 다듬고 함수를 쪼개고 중복을 제거하고 이름을 바꾼다. 그 과정에서 단위테스트를 모두 통과해야한다. 그리고 위에서 말한 것처럼 깔끔한 함수가 얻어질 것이다.
master programmer는 시스템을 구현할 프로그램이 아니라 풀어갈 이야기로 여긴다.
이 방식대로 만든다면 짧고 체계가 잡힌 함수가 나올 것이다. 하지만 진짜 목표는 시스템이라는 이야기를 풀어가는 데 있다는 사실을 잊지마라
* 클린 코드는 결국 시스템을 풀어나가는 이야기다. 그걸 항상 명심하자
'책과 강연 > 클린 코드' 카테고리의 다른 글
클린 코드 8장 : 경계 (1) | 2023.12.29 |
---|---|
클린코드 7장 : 오류 처리 (0) | 2023.12.28 |
클린코드 6장 : 객체와 자료구조 (0) | 2023.12.24 |
클린 코드 5장: 형식 맞추기 (0) | 2023.12.24 |
클린코드 4장 : 주석 (0) | 2023.12.24 |