-
Notifications
You must be signed in to change notification settings - Fork 0
Description
그동안 다뤄온 내용을 간단하게 정리해보는 장이다.
주석
- 적절한 정보 담기. 코드와 설계에 관하여 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.)
- 추상화 정도에 맞는 이름 고르기.
public interface Modem {
boolean dial(String phoneNumber); boolean disconnect();
boolean send(char c);
char recv();
String getConnectedPhoneNumber();
}만약 위와 같은 모뎀 인터페이스가 있는데, 어떤 프로그램에서는 dial이 아닌 인터넷 선으로 연결하는 모뎀일 수 가 있다. 그러니 범용성을 갖추기 위해서 위의 모뎀은 다음과 같이 renaming이 필요하다.
public interface Modem {
boolean connect(String connectionLocator); boolean disconnect();
boolean send(char c);
char recv();
String getConnectedLocator();
}관련 회사 경험이 하나 있는데... 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된다.
- 빠른 테스트