나쁜 코드
- 성능이 나쁜 코드
- 불필요한 연산이 들어가서 개선의 여지가 있는 코드
- 의미가 모호한 코드
- 이해하기 어려운 코드, 네이밍과 그 내용이 다른 코드
- 중복된 코드
- 비슷한 내용인데 중복되는 코드들은 버그를 낳는다.
- 깨진 유리창 법칙 - 나쁜 코드는 유리창 처럼 계속 나쁜 코드가 만들어지도록 한다.
- 생산성 저하 - 나쁜 코드는 팀 생산성을 저하시킨다.
- 기술부채를 만들어 수정을 더 어렵게 한다.
- 결국 새로운 시스템을 만들어야 한다.
시간이 없다고 나쁜 코드를 사용하면, 결국 일정을 못맞춘다.
클린 코드
- 성능이 좋은 코드
- 의미가 명확한 코드 - 가독성이 좋은 코드
- 중복이 제거된 코드
- 보이스카우트 룰 : 전보다 더 깨끗한 상태로 만든다.
의미가 분명한 이름 짓기
- 이름을 잘 짓는 간단한 규칙
- 소프트웨어에서 이름은 어디서나 쓰인다.
의도를 분명히 밝혀라
int d; // 경과 시간(날짜)
int daysSinceCreation;
이름 d는 아무 의미도 드러나지 않는다. 측정하려는 값과 단위를 표현하는 이름이 필요하다.
public List<int[]> getThem(){
List<int[]> list1 = new ArrayList<int[]>();
for (int[] x : theList)
if (x[0] == 4)
list.add(x);
return list1;
}
변수는 세개, 상수는 두 개뿐이지만, 코드의 단순성이 아닌 코드의 함축성에 문제가 있다.
- theList에 무엇이 들었는가?
- theList에서 0번째 값이 어째서 중요한가?
- 값 4는 무슨 의미 인가?
- 함수가 반환하는 list1을 어떻게 사용하는가?
public List<int[]> getFlaggedCells(){
List<int[]> flaggedCells = new ArrayList<>();
for (int[] cell : gameBoard)
if (cell[STATUS_VALUE] == FLAGGED)
flaggedCells.add(cell);
return flaggedCells;
}
코드의 단순성은 변하지 않으면서 코드는 명확해졌다. (지뢰찾기 게임 예시)
public List<Cell> getFlaggedCells(){
List<Cell> flaggedCells = new ArrayList<>();
for (Cell cell : gameBoard)
if (cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}
int 배열 대신 Cell 클래스를 만들어서 사용하고, isFlagged() 메서드를 만들어서 FLAGGED라는 상수를
감춰서 좀 더 개선할 수 있다.
단순 이름만 고쳤는데도 함수가 하는 일을 이해하기 쉬워졌다.
변수명에 타입 넣지 않기
List, Map 의 경우는 변수명을 사용하기도 한다.
구현체에 Impl 네이밍하는 방법은 아직까지 사용중이기도 하다.
의미 있게 구분하라
- 연속된 숫자를 덧붙이거나 불용어(noise word)를 추가하는 방식은 부적절하다.
- a1, a2, ... , aN은 의도적인 이름과 정반대다.
- 저자 의도가 전혀 드러나지 않는다.
public static void copyChars(char a1[], char a2[]){
for (int i=0; i<a1.length; i++){
a2[i] = a1[i]
}
}
함수 인수 이름으로 불용어를 추가한 예시
- 불용어는 중복이다.
- getActiveAccount(); , getActivateAccounts(); 와 같은 메서드가 있다면 읽는 사람의 입장에선 차이를 알기 어렵다. 차이를 알기 쉽게 이름을 지어라.
발음하기 쉬운 이름을 사용하라
private Date genymdhms; vs private Date generationTimestamp;
당연히 두 번째 이름이 옳다. 그래야지만 의사소통이 가능해진다.
자신의 기억력을 자랑하지 마라
- 루프에서 반복 횟수를 세는 변수 i, j, k는 괜찮다. (l은 절대 안된다. 숫자1과 헷갈리기도..)
- 한 단어를 두가지 목적으로 사용하지 말자.
- add 메서드가 생성된 후, 추가 메서드가 같은 맥락이 아니라면 insert, append 와 같은 네이밍 사용
클래스 이름
- 클래스 이름과 객체 이름은 명사나 명사구가 적합
- Customer, WikiPage, Account, AddressParser --- 옳은 네이밍
- 동사는 사용하지 않는다.
메서드 이름
- 메서드 이름은 동사나 동사구가 적합
- postPayment, deletePage, save --- 옳은 네이밍
- 접근자, 변경자, 조건자는 javabean 표준에 따라 get, set, is를 붙인다.
Complex fulcrumPoint = Complex.FromRealNumber(23.0);
Complex fulcrumPoint = new Complex(23.0);
아래 코드가 위코드 보다 좋은 코드이다.
생성자를 중복정의 할 때는 정적 팩토리 메서드를 사용한다.
생성자를 제한하려면 해당 생성자를 private 으로 막는다. (싱글톤)
의미 있는 맥락을 추가하라
- 스스로 의미가 분명한 이름이 없지 않지만, 대다수 이름은 그렇지 못한 경우가 많다.
- 클래스, 함수, 이름 공간에 넣어 맥락을 부여하고, 모든 방법이 실패하면 마지막 수단으로 접두어를 붙인다.
- firstName, street, city, state 를 개별적으로 봤을 땐 주소라는 사실을 알기 어렵다.
- addrFirstName, addrLastName 과 같이 분명한 네이밍을 하자.
- Address 클래스를 생성하는 방법도 존재한다.
private void printGuessStatistics(char candidate, int count) {
String number;
String verb;
String pluraModifier;
if (count==0){
number = "no";
verb = "are";
pluralModifier ="s";
}
else if (count==1){
number = "1";
verb = "is";
pluralModifier="";
}
...
String guessMessage = String.format(
"There %s %s %s %s", verb, number, candidate, pluraModifier);
print(guessMessage);
}
해당 메서드는 맥락이 불분명하다. 변수의 의미를 파악하기 위해 독자가 맥락을 유추해야만 한다.
그냥 메서드만 훓어서는 세 변수의 의미가 불분명하다.
public class GuessStatisticMessage {
private String number;
private String verb;
private String pluralModifier;
public String make(char candidate, int count){
createPluralDependentMessageParsts(count);
return String.format(
"There %s %s %s %s", verb, number, candidate, pluraModifier);
}
private void createPluralDependentMessageParsts(int count){
if (count == 0){
thereAreNoLetters();
}
... 이하 생략
}
private void thereAreNoLetters(){
number = "no";
verb = "are";
pluralModifier = "s";
}
}
함수를 작은 조각으로 쪼개고자 GuessStatisticMessage라는 클래스를 만든 후 세 변수를 클래스에 넣었다.
맥락이 분명해졌으며, 함수를 쪼개기가 쉬워지므로 알고리즘도 자동으로 더 명확해진다.
구글 자바 네이밍 가이드
Package Naming Guide
- 모두 소문자로 사용하며, _를 사용하지 않는다.
Class Namging Guide
- 대문자로 시작하는 카멜 케이스 사용
- 클래스는 명사, 명사구
- Character, ImmutableList
- 인터페이스는 명사, 명사구, (형용사도 가능)
- List, Readable
- 테스트 클래스는 Test로 끝나기
- HashTest, HashIntegrationTest
Method Naming Guide
- 소문자로 시작하는 카멜 케이스
- 메서드는 동사, 동사구
- sendMessage, stop
- junit 테스트에 underscore 사용되기도 한다.
- pop_emptyStack
본 포스팅은 Clean Code (Robert C. Martin), 제로베이스-한달한권(클린코드)를 참고하여 작성하였습니다.