Hack my life
[Clean Code] 의미 있는 이름-프로그래밍 네이밍방법 본문
*이 글은 책 Clean Code를 기반으로 작성하였습니다.
*Java 문법을 알고 있어야 이해하기 쉽습니다.
1. 프로그래밍 명명 규칙을 배워야 하는 이유
- 프로그래밍을 할 때 이름이 굉장히 많이 쓰인다. 변수에도 이름을 붙이고, 함수, 임수, 클래스, 패키지 에도 이름이 필요하다. 심지어 소스파일, 소스파일이 담긴 디렉터리에도 이름이 필요하다.
- 이렇게 이름이 많이 쓰이므로 이름을 잘 지으면 프로그래밍을 할 때 아주 편하다.
2. 의도를 분명히 밝혀라
- 조금 시간이 들더라도 이름을 지을때는 그 것의 존재 의도, 기능, 사용법이 잘 드러나게 지어야 한다.
1 | int d; //경과 시간(단위: 날짜) | cs |
d 라는 이름은 그 자체만 봤을 때 아무 의미도 드러나지 않는다. 다음과 같이 바꿀 수 있다.
1 2 3 4 | int elapsedTimeInDays; int daysSinceCreation; int daysSinceModification; int fileAgeInDays; |
- 의도가 드러나는 이름을 사용하면 코드 이해와 변경이 쉬워진다. 다음 코드를 보자
1 2 3 4 5 6 7 8 9 | public List<int[]> getThem() { List<int[]> list1 = new ArrayList<int[]>(); for (int[] x : theList) { if (x[0] == 4) { list1.add(x); } } return list1; } | cs |
코드의 공백과 들여쓰기도 적당하고 변수, 상수도 적지만 코드가 하는 일을 짐잦하기 어렵다.
문제는 코드의 단순성이 아니라 코드의 함충성이다.
이름들의 의도가 불분명해서 코드의 맥락이 없다. 위 코드는 암암리에 독자가 다음과 같은 정보를 안다고 가정한다
- theList에 무엇이 들어있는가?
- theList에서 0번째 값이 어째서 중요한가?
- 값 4는 무슨 의미인가?
- 함수가 반환하는 리스트 list1을 어떻게 사용하는가?
지뢰 찾기 게임을 만든다고 가정하자, theList는 게임판이다. 게임판에서 각 칸은 단순 배열로 표현한다.
배열에서 0번째 값은 칸의 상태를 뜻한다. 값 4는 깃발이 꽂힌 상태를 가리킨다.
상황을 알았으니 이름을 다시 써보자.
1 2 3 4 5 6 7 8 9 | public List<int[]> getFlaggedCells() { List<int[]> flaggedCells = new ArrayList<int[]>(); for (int[] cell : gameBoard) { if (cell[STATUS_VALUE] == FLAGGED) { flaggedCells.add(cell); } } return flaggedCells; } | cs |
코드의 구조는 동일하나 위 코드보다 훨씬 명확해졌다
한걸음 나아가 int 배열 대신 칸을 클래스로 만들어주고 isFlagged라는 좀 더 명시적인 함수를 사용해 FLAGGED라는 상수를 감춰도 좋을 것 같다.
1 2 3 4 5 6 7 | public List<int[]> getFlaggedCells() { List<Cell> flaggedCells = new ArrayList<Cell>(); for (Cell cell : gameBoard) if (cell.isFlagged()) flaggedCells.add(cell); return flaggedCells; } | cs |
처음보다 코드를 이해하기가 굉장히 쉬워졌다.
3. 그릇된 정보를 피하라
- 프로그래머에게 특수한 의미를 가지는 단어(List등)는 실제 컨테이너가 List가 아닌 이상 accountList와 같이 변수명에 붙이지 말자. 차라리 accountGroup, bunchOfAccounts, accounts등으로 명명하자,
- 중의적으로 해석될 수 있는 이름은 짓지 않는다. 직각삼각형의 빗변을 구할 때 hypotenuse를 hp로 줄이는 것이 훌륭한 약어로 보일지라도 hp라는 이름은 독자에게 그릇된 정보를 제공한다.
- 서로 흡사한 이름을 사용하지 않도록 조심한다.
4. 의미있게 구분하라
- 숫자 하나로 구분하지 말자 a1, a2, a3... 등등
- 의미없는 구분을 하지말자:
- Name VS NameString
- getActiveAccount() VS getActiveAccounts() VS getActiveAccountInfo()
- money VS moneyAmount
- message VS theMessage
5. 발음하기 쉬운 이름을 사용하라
- 프로그래밍은 사회적 활동이다. 발음하기 어려운 이름은 본인의 사고에도 방해가 되고 사람들과 협업할 때는 더욱 그렇다.
1 2 3 4 5 6 7 | // Bad class DtaRcrd102 { private Date genymdhms; private Date modymdhms; private final String pszqint = "102"; /* ... */ }; | cs |
1 2 3 4 5 6 7 | // Good class Customer { private Date generationTimestamp; private Date modificationTimestamp; private final String recordId = "102"; /* ... */ }; | cs |
6. 검색하기 쉬운 이름을 사용하라
- 상수를 여러 곳에서 사용한다면 이름을 붙여 사용한다. 디버깅을 할때 편하다. 5라는 상수를 썼을때 5를 검색하며 디버깅 할 모습을 상상해라
- 변수의 이름의 길이는 변수의 범위에 비례해서 길어진다. 검색하기 편하기 때문
7. 인코딩(변수에 부가 정보를 덧붙여 표기하는것)을 피해라
- 헝가리안 표기법을 피하라, 요즘은 IDE의 기능이 굉장히 좋아졌기 때문에 타입을 유추할수 있는 표기법이 필요가 없어졌다.
- 멤버 면수에 접두어를 붙이지 않는다.
1 2 3 4 5 6 7 | //Bad public class Part { private String m_dsc; void setName(String name) { m_dsc = name; } } | cs |
1 2 3 4 5 6 7 | //Good public class Part { private String description; void setDescriptione(String description) { this.description = description; } } | cs |
- 인터페이스 이름에 접두어를 붙이지 않는다. IShapeFactory VS ShapeFactory
8. 자신의 기억력을 자랑하지 말라
- 독자가 머리속으로 한번 더 생각해 변환해야 할만한 변수명을 쓰지 말라.
- 한글자 변수이름은 문제가 있다(루프에서 반복문 횟수를 세는 변수 i,j,k 제외)
- 똑똑한 프로그래머와 전문가 프로그래머를 나누는 기준 한가지는 Clarity(명료함)이다.
9. 클래스 이름
- 명사 혹은 명사구를 사용하라.(Customer, WikiPage, Account, AddressParser)
- Manager, Processor, Data, Info와 같은 단어는 피하자
- 동사는 사용하지 않는다.
10. 메서드 이름
- 동사 혹은 동사구를 사용하라.(postPayment, deletePayment, deletePage, save 등)
- 접근자, 변경자, 조건자는 get, set, is로 시작하자.
1 2 3 | String name = employee.getName(); customer.setName("mike"); if(paycheck.isPosted());... | cs |
- 생성자를 오버로드할 경우 정적 팩토리 메서드를 사용하고 해당 생성자를 private으로 선언한다.
11. 기발한 이름은 피하라
- 특정 문화에서만 사용되는 재미있는 이름보다 의도를 분명히 표현하는 이름을 사용하라
- HolyHandGrenade → DeleteItems
- whack() → kill()
12. 한 개념에 한 단어를 사용하라
- 똑같은 기능의 메서드를 어떤 클래스에서는 get 어떤 클래스에서는 retrieve, fetch등으로 제각각 부르면 굉장히 혼란스럽다.
- controller, manager, driver 도 마찬가지이다. 따라서 메서드의 이름은 일관적이어야 한다.
13. 말장난을 하지마라
- add라는 의미가 기존 두 값을 더하거나 이러서 새로운 값을 만드는데 쓰였다고 하자. 새로 작성하는 메서드는 집합에 값 하나를 추가한다. 일관성을 고려한답시고 이 메서드를 add라고 부르면 안된다.
- 즉 같은 맥락이 아닌데도 일관성을 고려한다고 같은 단어를 선택하면 안된다
14. 해법 영역에서 가져온 이름을 사용하라
- 코드를 읽는 사람은 프로그래머다. 따라서 프로그래머라면 알만한 용어들은 사용해도 좋다.
- 알고리즘 이름, 전산 용어, 수학 용어, 등등
- JobQueue, AccountVisitor(Visitor pattern)
15. 문제 영역에서 가져온 이름을 사용하라
- 적절한 프로그래머 용어가 없거나 문제영역과 관련이 깊은 용어의 경우 문제 영역 용어를 사용하자.
16. 의미 있는 맥락을 추가하라
- 클래스, 함수, namespace등으로 감싸서 맥락(Context)을 표현하라
- 그래도 불분명하다면 접두어를 사용하자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // Bad private void printGuessStatistics(char candidate, int count) { String number; String verb; String pluralModifier; if (count == 0) { number = "no"; verb = "are"; pluralModifier = "s"; } else if (count == 1) { number = "1"; verb = "is"; pluralModifier = ""; } else { number = Integer.toString(count); verb = "are"; pluralModifier = "s"; } String guessMessage = String.format("There %s %s %s%s", verb, number, candidate, pluralModifier ); print(guessMessage); } | cs |
변수들의 의미와 전체적인 맥락이 잘 들어오지 않는다. Address라는 클래스를 만들어서 감싸보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | // Good public class GuessStatisticsMessage { private String number; private String verb; private String pluralModifier; public String make(char candidate, int count) { createPluralDependentMessageParts(count); return String.format("There %s %s %s%s", verb, number, candidate, pluralModifier ); } private void createPluralDependentMessageParts(int count) { if (count == 0) { thereAreNoLetters(); } else if (count == 1) { thereIsOneLetter(); } else { thereAreManyLetters(count); } } private void thereAreManyLetters(int count) { number = Integer.toString(count); verb = "are"; pluralModifier = "s"; } private void thereIsOneLetter() { number = "1"; verb = "is"; pluralModifier = ""; } private void thereAreNoLetters() { number = "no"; verb = "are"; pluralModifier = "s"; } } | cs |
17. 불필요한 맥락을 없애라
- Gas Station Delux 이라는 어플리케이션을 작성한다고 해서 클래스 이름의 앞에 GSD를 붙이지는 말자. G를 입력하고 자동완성을 누를 경우 모든 클래스가 나타나는 등 효율적이지 못하다.
- 위 a처럼 접두어를 붙이는 것은 모듈의 재사용 관점에서도 좋지 못하다. 재사용하려면 이름을 바꿔야 한다.(eg, GSDAccountAddress 대신 Address라고만 해도 충분하다.)