의미있는 이름을 연구하자.
소프트웨어에서 이름은 변수, 함수, 클래스, 패키지 등 모든 곳에서 사용되며 이름을 '잘'지을수록 여러모로 편하다.
개인적 감상평
2장은 좋은 이름을 짓기위한 팁들을 알려준다.
이는 이름자체가 로직에 영향을 주는게 아니기에 얼핏 무시할수도 있고, 처음에는 오히려 로직짜는것보다 이름짓는 것 때문에 시간을 더 쓰는일이 있어서 적당히 작성하고 넘어가는 일이 많았다. 하지만, 그 후폭풍으로 오랜만에 코드를 수정해야 하거나 다른 개발자에게 코드를 알려줄 때 나부터 이게 무슨함수였는지 기억이 잘 나지않아 헤맸던 기억이 있다.
하지만 정말 이름과 변수를 의미있게 잘지었다면 나한테 문의가 올 일 자체가 없지 않았을까싶다. 클린코드가 내 습관을 고치는 일환이지만 그 중 좋은 이름 짓기는 조금 더 밀접한 내 고질적인 안좋은 습관을 고치라는 충고같이 느껴진다.
1.의도가 분명한 이름을 지어라
: 이름만으로 해당 변수나 함수등의 목적을 알 수 있어야 한다. 한 번에 못알아보거나 주석을 읽어야지만 알아보는 이름은 좋은 이름이 아니다.
//worst case
int d; //경과 시간(단위: 날짜)
//good case
int daysSinceCreation;
Java
복사
//worst case
public List<int[]> getThem(){
List<int[]> list1 = new ArrayList<int[]>();
for(int[] x: theList){
if(x[0]==4)
list1.add(x);
}
return list1;
}
//normal case
public List<int[]> getFlaggedCells() {
List<int[]> flaggedCells = new ArrayList<int[]>();
for (int[] cell gameBoard)
if (cell[STATUS VALUE] == FLAGGED)
flaggedCells.add(cell);
return flaggedCells;
}
//best case
public List<Cell> getFlaggedCells() {
List<Cell> flaggedCells = new ArrayList<Cell>();
for (Cell cell gameBoard)
if (cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}
Java
복사
⇒ 둘 다 지뢰찾기 게임 예제 코드인데 worst case는 로직이 어렵진 않지만 무슨 메소드인지 내부의 변수가 무엇을 의미하는지 알 수가 없다.
2. 그릇된 정보를 피하라.
•
널리 쓰이는 단어를 다른 의미로 사용하지 말아라.
public String getAccountList(){
return "catsbi,study,CleanCode";
}
Java
복사
⇒ getAccountList라 명명하면 컬렉션객체인 List 타입이 올 것이라고 추측하는데 다른 타입이 온다면 프로그래머에게 그릇된 정보를 제공하는 셈이다.
•
흡사한 이름을 사용하지 않도록 주의한다.
Ex)한 모듈에 getAccountJoinedDateOfString과 getAccountExpireDateOfString 과 같이 비슷한 이름을 같이 사용한다면 프로그래머는 실수할 가능성이 기하급수적으로 높아진다.
3. 의미 있게 구분하라
•
저자의 의도가 드러나지 않는 의미없는 네이밍을 자제하라
//worst case
public void copyChars(char a1[], char a2[]){
for (int i = 0; i < al. length; i++) {
a2[i] = a1[i];
}
}
//good case
public void copyChars(char source[], char destination[]){
for (int i = 0; i < al. length; i++) {
destination[i] = source[i];
}
}
Java
복사
⇒ 의미없는 인자값 a1과 a2 만 봐서는 어디서 어디로 복사하려는지 알 수 없고 무슨 인자인지도 알 수없다. 이 인자를 source[], destination[]으로 바꾸면 각각의 인자의 의미와 어디로 복사되는지 명료하게 알 수 있다.
•
불필요한 불용어(noise word)를 사용하지말아라
⇒ 의미있는 불용어(ex: 모든 지역변수는 a, 모든 함수 인수는 the 를 사용한다.)
⇒ 불필요한 불용어(ex: Product 클래스가 있는데ProductInfo 클래스 생성, zork 변수를 변수라는 이유만으로 theZork로 명명)
•
의미 없는 구분 네이밍 예시
⇒ 프로젝트에 참여한 프로그래머는 이 메소드 이름만 보고 어느 함수를 호출할지 알 수 있을까?
getActiveAccount();
getActiveAccounts();
getActiveAccountInfo();
Java
복사
4. 발음하기 쉬운 이름을 사용하라
genymdhms (generate date, year, month, day, hour. minute, second)
- 젠 야 무다 힘즈(?)
: 발음하기도 어려운 축약어및 네이밍은 의사소통을 불편하게하고 새로운 개발자와의 의사소통을 막는 주범이다.
예시 코드
//worst case
class DtaRcrd102 {
private Date genymdhms;
private Date modymdhms;
private final 5tring pszqint = "102";
...
};
//good case
class Customer {
private Date generationTimestamp;
private Date modificationTimestamp;
private final 5tring recordld = "102";
...
}
Java
복사
5. 검색하기 쉬운 이름을 사용하라
이름의 길이는 범위크기에 비례해야 한다.
•
변수나 상수를 코드 여러곳에서 사용한다면 검색하기 쉬운 이름으로 짓도록하자.
//worst case
for(int i = 0; i< 34; i++){
s += (t[i]*4)/5;
}
//good case
int realDaysPerldealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum 0;
for (int i=0; i < NUMBER_OF_TASKS; i++) {
int realTaskDays = taskEstimate[i] * realDaysPerldealDay;
int realTaskWeeks = (realTaskDays / WORK_DAYS_PER_WEEK) ;
sum += realTaskWeeks;
}
Java
복사
6. 인코딩을 피하라
안그래도 많은 의미를 담고있는 이름에 유형이나 범위정보까지 넣으면 해독의 난이도만 높아진다.
•
변수에 타입정보까지 포함하면 유연성이 떨어진다.
//헝가리안 표기법 사용 네이밍
private int icars;
Java
복사
⇒ 헝가리안 표기법에서 i접두사는 int 자료형을 의미한다. 하지만, iCars가 Car의 id를 담은 List<Integer> 타입이 된다면 이름까지 바꿔줘야하며 가독성도 떨어지게 된다.
•
불필요한 접수어를 붙히지 말아라.
//worst case
public class Part{
private String m_dsc; //설명 문자열
void setName(String name){
m_dsc = name;
}
}
//good case
public class Part{
String description;
void setDescription(String description){
this.description = description;
}
}
Java
복사
⇒ 더하여 개발자들은 접두어를 무시하고 이름을 해독하는 방식을 익히기에 접두어는 유명무실해진다.
•
인코딩이 필요한 경우도 있다.
: 인터페이스 클래스와 구현 클래스와 같은 경우 명세 클래스(API)인 인터페이스는 최대한 원형 그대로의 목적 네이밍으로 작성하고 구현 클래스에서 접두사나 접미사를 붙혀 구분한다.
◦
도형 생성 인터페이스: ShapeFactory
◦
도형 구현 클래스: ShapeFactoryImpl 이나 CShapeFactory 등
7. 자신의 기억력을 믿지 말아라
: 아주 작은 범위의 제한적인 상황에서의 i, j, k같은 이름은 괜찮지만 그외의 경우에 자신만 아는 이름으로 정하여 의미와는 연관되지않는 변수명을 사용하면 내가 잊어먹거나 다른사람이 코드를 만질 때 문제가 발생할 수 있다.
•
Ex) 쇼핑몰 유저의 이름들을 r 이라는 변수에 저장한다면 분명 까먹고 문제가 생길 것이다.
8. 클래스명은 명사나 명사구
•
Customer, WikiPage, Account, Address
9. 메소드명은 동사나 동사구
•
postPayment, deletePage, save
•
접근자(Accessor), 변경자(Mutator), 조건자(Predicate)는 값 앞에 get, set, is를 붙힌다.
•
생성자 중복정의시 정적 팩토리 메소드를 이용
//before case
Complex fulcrumPoint = new Complex(23.0);
//after case
Complex fulcrumPoint = Complex.FromRealNumber(23.0);
Java
복사
10. 기발한 이름은 피하라.
•
자기자신만 아는 유머나 속어들로 변수, 함수명을 짓는다면 해당 유머나 속어를 모르는사람은 코드를 읽기 어렵다.
//치약맛 케익 가격
int toothpasteCakePrice = 300000;
//민트초코 케익 가격
int MintChocoCakePrice = 30000;
Java
복사
내가 민트초코를 치약맛이라고 생각한다고 변수명을 치약맛케익가격이라고 선언해 사용한다면,
다른 사람들이 다 알아들을 수 있을까? 명확하게 민트초코케익가격이라 선언하자.
11. 한 개념은 한 단어를 사용하라.
같은 개념을 제각각 다른 이름으로 부르면 혼란을 야기한다.
Ex) 가져온다는 의미의 메소드 접두사를 get, fetch, retrieve를 제각각 쓴다면 나쁜코드이다.
일관성 있는 어휘는 코드를 사용할 프로그래머의 생산성을 높힐 수 있다.
12. 한 단어를 두 가지 목적으로 사용하지 마라.
: 일관성을 지킨다는 미명아래 add라는 단어를 기존에는 두 값을 더한 값을 반환하거나 기존의 값에 새로운 값을 더해서 쓰다가 새로 작성하는 add에서는 집합에 값을 추가하는기능으로 사용한다면 일관성을 깨트린다. 차라리 insert나 append가 적당하다.
코드는 독자가 해독해야하는게아니라 코드가 독자에게 의도를 밝힐 수 있어야 한다.
13. 해법 영역에서 가져온 이름을 사용하라.
: 코드를 읽는 사람도 프로그래머이기에 전산 용어, 알고리즘 이름, 패턴 이름, 수학 용어를 사용하는것은 괜찮다. 하지만 이름을 문제 영역(domain)에서 가져온다면 매번 고객에게 의미를 물어야한다.
디자인패턴의 Visitor Pattern에 익숙한 개발자는 AccountVisitor를 금새 이해한다.
14. 문제 영역에서 가져온 이름을 사용하라.
: 적절한 프로그래머 용어가 없다면(해법 영역에 적절한 이름이 없다면) 문제 영역에서 이름을 가져온다. 그렇다면 개발자가 바로 이름을 알 수는 없어도 전문가에게 물어 바로 파악할 수 있다. 즉, 문제 영역 개념과 관련이 깊은 코드람녀 문제 영역에서 이름을 가져오는게 맞다.
15. 의미있는 맥락을 추가하라.
•
클래스, 함수, 이름 공간에 부여해 맥락을 부여하는게 안된다면 접두사를 통해 맥락을 부여한다.
firstName, lastName, street, houseNumber, city, state, zipcode
Java
복사
⇒ 전체를 보면 주소(Address)라는것을 알 수 있지만, 이 중 하나만 꺼내 사용한다면 주소의 일부라는 것을 알기 힘들다. 여기에 addr이라는 접두사를 붙힌다면 맥락을 좀 더 명확히 부여할 수 있다.
addrFirstName, addrLastName, addrStreet, addrHouseNumber, addrCity, addrState, addrZipcode
Java
복사
⇒ 물론 Address라는 클래스에 속하게한다면 더 좋다.
16. 불필요한 맥락을 없애자.
•
클래스에는 불필요한 맥락이 없도록하자.
: accountAddress나 customerAddress보다는 Address가 적합하다.