[책] 클린코드 - 애자일 소프트웨어 장인 정신 (4, 5, 6장)
클린코드(w.로버트 C. 마틴)를 읽고 개인적인 학습을 위해 요약한 글 입니다. 문제 시 하단에 댓글 달아주시면 감사하겠습니다.
4. 주석
잘 달린 주석은 그 어떤 정보보다 유용하지만, 담당자가 주석까지 업데이트하기란 쉽지 않다. 그렇기 때문에, 코드를 깔끔하게 정리하고 표현력을 강화하는 방법이 가장 좋은 방식이다. 애초에 주석이 필요 없도록. 코드만이 정보를 제공하는 유일한 출처이기 때문이다.
주석은 나쁜 코드를 보완하지 못한다
주석을 추가하는 이유는 코드 품질이 낮기 때문이다. 표현력이 풍부하고 깔끔하며 주석이 거의 없는 코드는 주석이 필요없다.
두개를 비교해보자.
//직원에게 복지 혜택을 받을 자격이 있는지 검사한다.
if ((employee.flags && HOURLY_FLAG) && (employee.age > 65))
이것과 비교하여,
if (employee.isEligibleForFullBenefits())
주석이 없어도 충분히 빠르게 읽힌다!
좋은 주석
유익한 주석은 무엇일까? (정말 좋은 주석은 달지 않을 방법을 고민한 주석이란 것)
- 법적인 주석
- 저작권 정보와 소유권 정보를 나타낸다.
- 정보를 제공하는 주석
- 이것보단 SimpleDateFormat.format 함수를 활용하면 더 깔끔
// kk:mm:ss EEE, MMM dd, yyyy형식이다. Pattern timeMatcher = Pattern.compile("\\d*:\\d*:\\d* \\w*, :\\w* \\d*, :\\d*);
- 의도를 설명하는 주석
- 의미를 명료하게 밝히는 주석
assertTrue(a.compareTo(a) == 0); //a==a
물론 그릇된 주석을 달면 위험은 상당히 높다. 각별히 주의하며 달아야한다. - 결과를 경고하는 주석
테스트를 돌릴 때 너무 오래 걸린다거나, 주의가 필요하다면 써줘야한다. JUnit에서 @Ignore 속성을 이용하여 @Ignore("실행이 너무 오래 걸린다.")라고 쓴다. - TODO주석
앞으로 할 일을 남겨둘 때 - 중요성을 강조하는 주석
- 공개API에서 Javadocs
나쁜 주석
- 주절거리는 주석
- 같은 이야기를 중복하는 주석
- 오해할 여지가 있는 주석
- 의무적으로 다는 주석
- 모든 함수에 Javadocs를 넣으라는 규칙. 오히려 더 헷갈리게 할 수 있다.
- 이력을 기록하는 주석
- 요즘은 git등이 있다. 이러한 방법은 요샌 혼란만 가중시킨다.
- 있으나 마나 한 주석
- 당연한 사실 언급이나 새로운 정보 제공을 못하는 주석
- 무서운 잡음
- 때로는 Javadocs도 잡음이다.
- 변수나 함수로 표현할 수 있다면 주석을 달지 마라
- 위치를 표시하는 주석
- 닫는 괄호에 다는 주석
- 공으로 돌리거나 저자를 표시하는 주석
- 주석으로 처리한 코드
- 남이 지우기 주저한다. 이러다 쌓인다.
- HTML 주석
- 전역 정보
- 너무 많은 정보
- 모호한 관계
- 주석과 주석이 설명하는 코드 사이 관계가 명백해야 한다. 무슨 소리인지 알 수 있도록.
- 함수 헤더
- 비공개 코드에서 Javadocs
5. 형식 맞추기
간단한 규칙을 정하고, 그 규칙을 착실히 따라야 한다.
목적
너무나 중요하므로 맹목적으로 따르면 된다. 가독성에 지대한 영향을 미친다.
적절한 행의 길이
세로 길이부터 살피자. 어느 길이가 적당할까. 책에서 말하는 프로젝트 별 분류에 따르면, 200줄 정도인 파일로도 커다란 시스템 구축이 가능하다고 말한다. 즉, 큰 파일보다 작은 파일이 이해가 쉬우므로 규칙 삼으면 좋을거다.
신문 기사처럼 작성하라
이름은 간단하게 설명이 가능하게. 이름만 보고도 내가 보려는 모듈인지 알 수 있게. 아랫줄로 내려갈수록 의도를 세세하게.
개념은 빈행으로 분리하라
package, import 등은 가독성을 위해 행 분리가 되어있는 것이다. 분리하는 것이 옳다.
세로 밀집도
불필요한 주석의 나열은 코드 전체가 한눈에 못들어오게 할 수 있다.
수직거리
변수선언. 변수는 사용 위치에 최대한 가까이 선언한다. 지역 변수는 각 함수 맨 처음에 선언한다.
인스턴스는 클래스 맨 처음에 선언한다. 변수 간에 세로로 거리를 두지 않는다.
종속함수는 호출하는 함수를 호출 되는 함수보다 먼저 배치하고, 그 함수들끼리 가까이 배치한다.
개념적인 친화도가 높은 함수는 가까이 배치한다.
가로 형식 맞추기
명백하게 짧은 행을 선호. 저자는 120자 정도로 제한.
수식은 우선순위에 따라 띄어쓰기를 하면 좋다. ex. return (-b - Math.sqrt(determinant)) / (2*a);
그냥 예시를 보면 밥 아저씨의 형식 규칙을 알 수 있다. (P.114)
6. 객체와 자료구조
자료 추상화
개발자는 객체가 포함하는 자료를 표현할 가장 좋은 방법을 심각하게 고민해야 한다. 아무 생각 없이 조회/설정 함수를 추가하는 방법이 가장 나쁘다.
구체적인 Vehicle 클래스
public interface Vehicle { double getFuelTankCapcityInGallons(); double getGallonsOfGasoline(); }
추상적인 Vehicle 클래스
public interface Vehicle { double getPercentFuelRemaining(); }
자료를 세세하게 공개하는 것 보다 추상적으로 표현하는 것이 좋다.
자료/객체 비대칭
객체와 자료구조는 상호보완적 특질이 있다. 책에서 절차적인 도형 클래스와 객체 지향적인 도형 클래스를 보여주고 있다.
절차적인 도형 클래스는 도형에 대한 간단한 자료구조만 제공하고, 도형의 동작 방식은 Geometry 코드에서 구현한다. 반면, 객체 지향적인 도형 클래스는 도형마다 area()라는 다형 메서드가 존재한다. 새 도형을 추가해도 기존 함수에 영향을 주지 않는다.
절차적인 코드는 기존 자료구조의 변경 없이 새 함수의 추가가 쉽다. 반면, 객체 지향 코드는 기존 함수를 변경하지 않으면서 새 클래스 추가가 쉽다. 반대로 말하면, 절차적인 코드는 새로운 자료 구조의 추가가 어렵다. 모든 함수를 고쳐야하기 때문이다. 반면, 객체 지향 코드는 새 함수 추가가 어렵다. 그러려면 모든 클래스를 다 고쳐야하기 때문이다.
새로운 자료 타입이 필요하면 클래스와 객체 지향 기법을, 새로운 함수가 필요하면 절차적인 코드와 자료 구조를 선택한다.
디미터 법칙
모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙이다. 객체는 자료를 숨기고 함수를 공개한다.
기차충돌
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
여러 객차가 한줄로 이어진 것처럼 보인다. 조잡하다 여겨지는 방식이라 피하는 편이 좋다. 이는 디미터 법칙을 어기는 듯이 보이기도 한다. 아래와 같이 나누는 것을 제안한다.
Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
위 코드는 디미터 법칙을 위반할까? opts, scratchDir, outputDir이 객체라면 내부 구조를 숨겨야 하므로 위반이다. 반면, 자료구조라면 내부 구조를 노출하므로 디미터 법칙이 적용되지 않는다.
final String outputDir = ctxt.options.scratchDir.absolutePath;
이렇게 함수없이 한다면 훨씬 간단해진다.
잡종구조
절반은 객체, 절반은 자료 구조인 잡종 구조가 나오기 십상이다. 새로운 함수 추가, 새로운 자료 구조 추가도 어렵다. 이런 어중간한 설계는 피하자.
구조체 감추기 -(?)
ctxt, options, scratchDir이 객체라면? 내부 구조를 감추어야 하므로 줄줄이 엮어선 안된다.
ctxt가 객체라면 뭔가를 하라고 말해야지 속을 들어내라 하면 안 된다. ctxt는 임시 파일 생성을 위해 디렉터리의 절대 경로를 얻으려는 목적을 갖고 있다는 것을 알 수 있다. ctxt 객체에 임시 파일을 생성하라고 시키면 적당해 보인다. 내부 구조를 드러내지 않고, 여러 객체를 탐색할 필요가 없다.
자료 전달 객체
자료 구조체의 전형적인 형태는 공개 변수만 있고 함수가 없는 클래스. DTO(Data Transfer Object)이다. DB 통신, 소켓에서 받은 메시지 분석 때 특히 유용하다.