비야네 스트롭스트룹 -> 보기에 즐거운 코드
그래디 부치 -> 잘 쓴 문장처럼 잘 읽혀야 한다. (가독성)
데이부 토마스 -> 가독성 + 다른 사람들이 코치기 쉬운 코드
마이클 페더스 -> 코드를 주의 깊게 짜는 방법
론 제프리스 -> 중복을 피해라, 한기능만 수행, 제데로 표현, 작게 추상화
워드 커닝햄 -> 읽으면서 놀랄일이 없어야 한다. 코드를 독해하느라 머리를 쥐어짤 필요가 없어야 한다. 읽으면서 짐작한 대로 돌아가는 코드가 깨끗한 코드이다.
: 처음 개발 속도는 느릴지 몰라도 추후 유지보수를 잘하기 위해 좋은 코드를 사용해야하는거 같다.
: 의도가 분명한 이름이 정말 중요하다. 좋은 이름을 지으려면 시간이 걸리지만 좋은 이름으로 절약하는 시간이 훨씬 많다. 변수나 함수, 클래스 이름은 다음과 같은 굵직한 질문에 모두 답해야한다. 변수의 존재 이유 ? 수행 기능 ? 사용 방법 ? 따로 주석이 필요하다면 의도를 분명히 들어내지 못했다는 말이다.
ex)
나쁜 예시
public List<int[]> getThem() {
List<int[]> list1 = new ArrayList<int[]>();
for( int[] x : theList) {
if(x[0] == 4) list1.add(x);
}
return list1
}
이렇게 코드를 작성했을 경우 4가지 의문점들이 들수 있다.
아래처럼 변수명만 바꾸어도 의도가 명확하게 드러난다.
gameBoard = 게임판
0의 값은 게임의 상태를 나타내는 값
4는 깃발이 꽂힌 상태
return 되는 값이 어떤 값인가.
public List<int[]> getFlaggedCells() {
List<int[]> flaggedCells = new ArrayList<int[]>();
for(int[] cell : gameBoard) {
if(cell[STATUE_VALUE) == FLAGGED) {
flaggedCells.add(cell)
}
return flaggedCells
}
}
위 코드에서 조금 더 보안하면 깃발이 꽂힌 상태를 체크하는 함수는 분리히면 좋을것이다.
최종 코드
public List<int[]> getFlaggedCells() {
List<int[]> flaggedCells = new ArrayList<int[]>();
for(int[] cell : gameBoard) {
if(cell.isFlagged()) {
flaggedCells.add(cell)
}
return flaggedCells
}
}
public Boolean int[].isFlagged() {
return this[STATUE_VALUE) == FLAGGED
}
: 프로그래머는 코드에 그릇된 단서를 남겨서는 안 된다. 그릇된 단서는 코드 의미를 흐린다. 나름대로 널리 쓰이는 의미가 있는 단어를 다른 의미로 사용해도 안된다.
예를 들어,hp,aix,sco 는 변수 이름으로 적합하지 않다. 유닉스 플랫폼이나 유닉스 변종을 가리키는 이름이기 때문이다. 직각 삼각형의 빗변을 구현할 때는 hp가 훌륭한 약어로 보일지라도 hp라는 변수는 독자에게 그릇된 정보를 제공한다. 서로 흡사한 이름을 사용하지 않도록 주의한다.
: 동일한 범위 안에서 다른 두 개념에 같은 이름을 사용하지 못한다. 그래서 프로그래머가 한쪽 이름을 마음대로 바꾸고픈 유혹에 빠진다. 이때 철자만 바꾸거나 연속된 숫자를 덧붙이지 말고 의미를 가지고 구분하는 것이 좋다.
: 사람들은 단어에 능숙하다. 우리 두뇌에서 상당 부분은 단어라는 개념만 전적으로 처리한다. 그리고 정의상으로 단어는 발음이 가능하다. 말을 처리하려고 발달한 두뇌를 활용하지 않는다면 안타까운 손해다. 그러므로 발음하기 쉬운 이름을 선택한다. 그럼 토론도 쉬워진다.
: 문자 하나를 사용하는 이름과 상수는 텍스트 코드에서 쉽게 눈에 띄지 않는다는 문제점이 있다.
예를들어, MAX_CLASSES_PER_STUDENT 는 grep으로 찾기가 쉽지만, 숫자 7은 은근히 까다롭다. 7이 들어가는 파일 이름이나 수식이 모두 검색되기 때문이다.
: 유형이나 범위 정보까지 인코딩에 넣으면 그만큼 이름을 해독하기 어려워진다. 대개 새로운 개발자가 익힐 코드 양은 상당히 많다. 문제 해결에 집중하는 개발자에게 인코딩은 불필요한 정신적 부담이다. 인코딩한 이름은 거의 다 발음하기 어여우며 오타가 생기기도 쉽다.
하지만 때로는, 인코딩이 필요한 경우도 있다. 예를 들어, 도형을 생성하는 ABSTRACT FACTORY 를 구현한다고 가정하자. 이 팩토리는 인터페이스 클래스다. 구현은 구체 클레스에서 한다. 그렇다면 두 클래스 이름을 어떨게 지어야 좋을까 ? IShapeFactory와 ShapeFactory ? 개인적으로 인터페이스 이름은 접두어를 붙이지 않는 편이 좋다고 생각한다. 옛날 코드에서 많이 사용하는 접두어 I는 주의를 흐트리고 과도한 정보를 제공한다. 나로서는 내가 다루는 클래스가 인터페이스 라는 사실을 남에게 알리고 싶지 않다. 클래스 사용자는 그냥 ShapeFactory 라고만 생각하면 좋겠다. 그래서 인터페이스 클래스 이름과 구현 클래스 이름 중 하나를 인코딩해야 한다면 구현 클래스 이름을 택하겠다.
: 독자가 코드를 읽으면서 변수 이름을 자신이 아는 이름으로 변환해야 한다면 그 변수 이름은 바람직하지 못하다. 이는 일반적으로 문제 영역이나 해법 영역에서 사용하지 않는 이름을 선택했기 떄문에 생기는 문제다. 변수 이름을 문자하나만 사용하면서 그변수의 의미를 기억하고 있지 마라.
: 클래스 이름과 객체 이름은 명사나 명사구가 적합하다. Customer, WikiPage, Account, AddressParser 등이 좋은 예이다. Manager, Processor, Data, Info 등과 같은 단어는 피하고 동사는 사용하니 않는다.
: 이름이 너무 기발하면 저자와 유머 감각이 비슷한 사람만, 그리고 농담을 기억하는 동안만, 이름을 기억한다. HolyHandGrenade 라는 함수가 무엇을 하는지 알겠는가 ? 기발한 이름이지만 DeleteItems가 더 좋다. 재미난 이름 보단 명료한 이름을 사용해라.
: 추상적인 개념 하나에 단어 하나를 선택해 이를 고수한다.
예를 들어, 똑같은 메서드를 클래스 마다 fatch, retrieve, get 으로 제각각 부르면 혼한스럽다. 어느 클래스에서 어느 이름을 썻는지 기억하기 어렵다.
: 한 단어를 두 가지 목적으로 사용하지 마라. 다른 개념에 같은 단어를 사용한다면 그것은 말장난에 불과하다. 한 개념에 한 단어를 사용하라는 규칙을 따랐더니,
예를 들어 여러 클래스에 add라는 메서드가 생겼다. 모든 add 메서드의 매개변수오 반환값이 의미적으로 똑같다면 문제가 없다. 하지만 떄로는 프로그래머가 같은 맥락이 아닌데도 일관성을 고려해 add라는 단어를 선택한다.
지금까지 구현한 add 메서드는 모두가 기존 값 두 개를 더하거나 이어서 새로운 값을 만든다고 가정하자. 새로 장석하는 메서드는 집합에 값 하나를 추가한다. 이 메서드를 add라 불러도 괜찮을까 ? add라는 메서드가 많으므로 일관성을 지키려면 add라 불러야하지 않을까 ? 하지만 새 메서드는 기존 add와 맥락이 다르다. 그러므로 insert나 append라는 이름이 적당하다. 새 메서드를 add 라 부르면 말장난이다.
: 코드를 읽을 사람도 프로그래머라는 사살을 명심한다. 그러므로 전산 용어, 알고리즘 이름, 패턴 이름, 수학 용어 등을 사용해도 괜찮다. 모든 이름을 문제 영역에서 가져오는 정책은 현명하지 못하다. 같은 개념을 다른 이름으로 이해하던 동료들이 매번 고객에게 의미를 물어야하기 때문이다. VISITOR 패턴에 친숙한 프로그래머는 AccountVisitor라는 이름을 금방 이해한다. JobOueue를 모르는 프로그래머가 있을까? 프로그래머에게 익숙한 기술 개념을 아무 많다. 기술 개념에는 기술 이름이 가장 적합한 선택이다.
: 적당한 프로그래머 용어가 없다면 문제 영역에서 이름을 가져온다. 그러면 코드를 보수하느 프로그해머가 분야 전문가에게 의미를 물어 파락할 수 있다. 우수한 프로그래머와 설계자라면 해법 영역과 문제 영역을 구분할 줄 알아야 한다. 문제 영역 개념과 관련이 깊은 코드라면 문제 영역에서 이름을 가져와야한다.
: 스스로 의미가 분명한 이름이 없지 않다. 하지만 대다수 이름은 그렇지 못하다. 그래서 클래서, 함수 이름 공간에 넣어 맥락을 부여한다. 모든 방법이 실패하면 마지막 수단으로 접두어를 붙인다.
예를 들어 firstName, lastName, street, houseNumber, city, state, zipcode 라는 변수가 있다. 변수를 훑어보면 주소라는 사실을 금방 알아챈다. 하지만 어드 메서드 state 라는 변수 하나만 사용한다면 ? 변수 State가 주소 일부라는 사실을 금방 알아챌까 ?
add라는 접두어를 추가해 addFirstName, addLastName,addState라 쓰면 맥락이 좀 더 분명해진다. 변수가 좀 더 큰 구조에 속한다는 사실이 적어도 독자에게는 분명하다. 물론 Address라는 클래스를 생성하면 더 좋다. 그러면 변수가 좀더 큰 개념에 속한다느 사실이 컴파일러에게도 분명해진다.
: 고급 휘발유 충전소(Gas Station Deluxe) 라는 어플을 만든다고 가정하고 모든 클래스 이름은 GSD로 시작하겠다는 생강은 전혀 바람직하지 못하다.
(IDE에서 G를 입력하고 자동 완성 키를 누르면 IDE는 모든 클래스르 열거한다. 현명하지 못하다. IDE는 개발자를 지원하는 도구다. IDE를 방해할 이유는 없다.)
: 좋은 이름을 선택하려면 설명 능력이 뛰어나야 하고 문화적인 배경이 같아야 한다. 이것이 제일 어렵다. 좋은 이름을 선택하는 능력은 기술, 비즈니스, 관리 문제가 아니라 교육 문제다. 우리 분야 사람들이 이름 짓는 방법을 제대로 익혀지 못하는 이유가 바로 여기에 있다.
사람들이 이름을 바꾸지 않으려는 이유 하나는 다른 개발자가 반대할까 두려워서다. 우리들 생각은 다르가. 오히려(좋은 이름으로 바꿔주면) 반갑고 고맙다. 우리들 대다수는 자신이 짠 클래스 이름과 메서드 이름을 모두 암기하지 못한다. 암기는 요즘 나오는 도구에게 맡기고, 우리는 문장이나 문단처럼 읽히는 코드 아니면 (정보를 표시하는 최선의 방법이 항상 문장만은 아니므로) 적어도 표나 자료 구조처럼 읽히는 코드를 짜는 데만 집중해야 마땅하다. 어느 코드 개선 노력과 마찬가지로 이름 역시 나름대로 바꿨다가는 누군가 질책할지도 모른다. 그렇다고 코드를 개선하려는 노력을 중단해서는 안 된다.
: 의미있는 이름을 사용하는 이유는 결국 타인 또는 미래의 본인이 코드를 봤을 때 더 잘 읽기 위함(가독성)이라고 생각이 든다