적절한 정보 담기. 코드와 설계에 관하여 technical하게 내용을 적는 공간이어야 한다. 안좋은 예: Source code control system, issue tracking system, 작성자
시간이 지나도 변하지 않는 정보 담기. 코드를 수정하다가 주석 수정은 잊어버릴 수 있기 때문에, 주석은 오래된 정보가 되기 쉽다. 만약 오래된 내용을 발견했다면, 업데이트하거나 삭제한다.
코드와 같은 내용은 담지 않기. 코드로 충분히 알수 있는 내용을 주석에 담는건 과하다.
단어, 문법, 구두법을 신경써서 간결하게 잘 작성하기. 알잘딱깔쎈?
주석처리한 코드를 보았다면 삭제하기. 어차피 source code control system이 기록하고 있으니 그 코드가 필요하다면 언제든 다시 불러올 수 있다.
환경
간단한 빌드와 테스트 단계. 여러 단계를 거치지 않고 단 하나의 command로 전체 빌드, 전체 테스트가 실행되어야 한다.
함수
최소한의 arguments
Output arguments 피하기
Flag arguments 피하기. Flag arguments를 가지는 함수라면, 그 함수는 하는일이 많다는 뜻이다.
안쓰는 함수 없애기
일반
하나의 소스코드에는 하나의 언어만 담기
예상되는 동작이 담기도록 함수/클래스 구현하기. The Principle of Least Surprise.
Boundary condition에 주의하기. 직관에 의존하지 말고 모든 boundary condition을 살피고 테스트도 작성하자.
Overridden Safeties. Safety warning 절대 무시하지 않기. 컴파일러, 테스트 실패 warning 등
중복 제거. 이 책의 가장 중요한 법칙 중 하나. DRY(Don't Repeat Yourself). 중복 코드가 있으면, 추상화할 기회라는 것이다. 내가 추상화 단계를 높여두면, 다른 개발자들이 빨리 코딩할 수 있고 에러도 줄어든다. Switch/case, if/else 체인은 다형성(polymorphism)으로 바꿀 수 있다.(?)
추상화 단계에 맞는 코드 짜기. 잘못된 예: 베이스 클래스에 상수, 변수, 유틸함수 같은 구체적인 구현체에 들어가야할 것들을 넣는 것
너무 많은 정보 담지 않기. 모듈이 잘 정의되어있다면 작은 인터페이스만 가질 것이다. 과하게 많은 함수를 갖고 있지 않을 것이므로 커플링정도도 낮을 것이다. 인터페이스에 무엇을 드러낼 것인지에 대한 limit 설정을 잘 하면 좋은 개발자.
변수는 사용되는 함수 근처 가까이에 정의하기.
일관성 갖추기. 비슷한 동작을 한다면 비슷한 방식으로 구현한다. The Principle of Least Surprise.
고의적 커플링 피하기. 종속성이 없는데도 편의를 위해서 커플링하는건 피해야한다.
Feature Envy.
public class HourlyPayCalculator {
public Money calculateWeeklyPay(HourlyEmployee e) {
int tenthRate = e.getTenthRate().getPennies();
int tenthsWorked = e.getTenthsWorked();
int straightTime = Math.min(400, tenthsWorked);
int overTime = Math.max(0, tenthsWorked - straightTime); int straightPay = straightTime * tenthRate;
int overtimePay = (int)Math.round(overTime*tenthRate*1.5);
return new Money(straightPay + overtimePay);
}
}
calculateWeeklyPay가 HourlyEmployee의 데이터를 여러번 가져온다. 차라리 이 함수가 HourlyEmployee 안에 있으면 클래스의 데이터를 외부로 노출시키지 않을 수 있게 되고 feature envy를 제거할 수 있게 된다. Feature envy를 제거하는 것이 마냥 좋을 것 같지만 사실 주의를 요하는 작업이다. 다음 예시를 보자.
public class HourlyEmployeeReport {
private HourlyEmployee employee ;
public HourlyEmployeeReport(HourlyEmployee e) {
this.employee = e;
}
String reportHours() {
return String.format(
"Name: %s\tHours:%d.%1d\n", employee.getName(), employee.getTenthsWorked()/10, employee.getTenthsWorked()%10);
}
}
reportHours가 HourlyEmployee에 있는게 이상적으로 보인다. 하지만 HourlyEmployee는 report format을 알 필요가 없다. feature envy를 제거하면, HourlyEmployee가 report format에 커플링이 생겨버린다.
Java
긴 Import lines 피하기. Bulk import 하기. IDE가 잘 접어줘서 이제는 크게 상관없는데.. 명시적 import가 낫지 않나? 한번 이야기 나누고 싶은 부분.
Constant는 상속되지 않게 따로 분리하기. 부모 클래스에 종속된 constant는 계속 상속이 될 수 있고 그럼 자식의 자식의 자식 클래스는 그 constant가 정의된 곳을 찾기 위해 상속 체인을 타고타고 올라가야 한다. 상수는 분리해서 static import하자. 예시: import static PayrollConstants.*;
Constant말고 enum쓰기. Java의 enum은 method와 field가 들어갈 수 있으니 문법에 주의할 것. Enum의 표현력과 융통성은 단순 상수(public static final int)보다 강력하다.
네이밍
설명이 충분히 되는 이름을 고르기. 시간을 충분히 들여서 이름을 신중하게 잘 고르면, 코드 구조가 이름에 드러나게 된다. 그렇게 되면 코드가 직관성을 갖게 된다. (It will be pretty much what you expected.)
관련 회사 경험이 하나 있는데... integrate하는 브랜드(Postgre, Microsoft, Amazon 등) 아이콘이 있고, 그 아이콘들은 connection에 쓰이는 것들이라서 prefix가 connection 이었다. 기능 구현팀에서는 이름을 기능 구현 의도를 살려서 그대로 두고 싶은데 디자인시스템 팀에서는 prefix를 logo 로 바꿨다.
회사 모든 개발자가 알만한 단어 사용하기. 개발자들이 아는 term도 좋은데 사내 term이 있다면 그걸 사용하면 더 쉽게 코드를 설명할 수 있다.
애매모호한 이름 쓰지 않기. 예) doRename() 대신 renamePageAndOptionallyAllReferences(). 길어도 이게 낫다.
Long names for Long Scopes. 간단한 한 줄짜리 for문 이라면 i를 써도 큰 문제가 없지만 긴 line을 다뤄야한다면 변수명이 길어져야 한다.
인코딩 피하기. m_, f, vis_ for visual imaging system 같은 prefix 피하기.
Side effect가 표현되게 이름짓기.
public ObjectOutputStream getOos() throws IOException { if (m_oos == null) {
m_oos = new ObjectOutputStream(m_socket.getOutputStream()); }
return m_oos;
}
위의 경우 단순히 Oos(ObjectOutputStream)를 리턴하는 함수가 아니다. ObjectOutputStream이 없으면 새로 만들기도 하기 때문에, getOos보다는 createOrReturnOos라고 이름짓는 게 낫다.
테스트
충분한 테스트. 단순히 "음, 충분한 것 같은데?" 라고 판단하고 넘길 것이 아니라 오류가 발생할 수 있는 모든 곳을 테스트 해야한다.
커버리지 테스트 툴 사용하기
자잘한 테스트 그냥 넘기지 않기
Test boundary condition 설정에 주의하기
버그가 발생했다면 그 함수를 철저하게 테스트하기. 버그 하나 발생했다고 하나만 있는게 아닐 수 있다. 아마 몇개 더 찾을걸
실패한 테스트에서 패턴 찾기. 테스트 케이스가 실패한 흐름에서 패턴을 발견할 수 있고, 거기에서 문제를 진단할 수 있다. 예: 모든 테스트에서 input string이 5자를 넘어갔을 때 fail된다.
그동안 다뤄온 내용을 간단하게 정리해보는 장이다.
주석
알잘딱깔쎈?환경
함수
일반
calculateWeeklyPay
가HourlyEmployee
의 데이터를 여러번 가져온다. 차라리 이 함수가HourlyEmployee
안에 있으면 클래스의 데이터를 외부로 노출시키지 않을 수 있게 되고 feature envy를 제거할 수 있게 된다. Feature envy를 제거하는 것이 마냥 좋을 것 같지만 사실 주의를 요하는 작업이다. 다음 예시를 보자.reportHours
가HourlyEmployee
에 있는게 이상적으로 보인다. 하지만HourlyEmployee
는 report format을 알 필요가 없다. feature envy를 제거하면,HourlyEmployee
가 report format에 커플링이 생겨버린다.Java
import static PayrollConstants.*;
public static final int
)보다 강력하다.네이밍
만약 위와 같은 모뎀 인터페이스가 있는데, 어떤 프로그램에서는 dial이 아닌 인터넷 선으로 연결하는 모뎀일 수 가 있다. 그러니 범용성을 갖추기 위해서 위의 모뎀은 다음과 같이 renaming이 필요하다.
관련 회사 경험이 하나 있는데... integrate하는 브랜드(Postgre, Microsoft, Amazon 등) 아이콘이 있고, 그 아이콘들은 connection에 쓰이는 것들이라서 prefix가 connection 이었다. 기능 구현팀에서는 이름을 기능 구현 의도를 살려서 그대로 두고 싶은데 디자인시스템 팀에서는 prefix를 logo 로 바꿨다.
doRename()
대신renamePageAndOptionallyAllReferences()
. 길어도 이게 낫다.i
를 써도 큰 문제가 없지만 긴 line을 다뤄야한다면 변수명이 길어져야 한다.m_
,f
,vis_
for visual imaging system 같은 prefix 피하기.위의 경우 단순히 Oos(ObjectOutputStream)를 리턴하는 함수가 아니다. ObjectOutputStream이 없으면 새로 만들기도 하기 때문에,
getOos
보다는createOrReturnOos
라고 이름짓는 게 낫다.테스트