의미 있는 이름의 중요성은 우리의 생활에서도 중요성을 발견할 수 있다.
배를 예시로 들어보겠다. 먹는 배, 타는 배, 사람의 배등 다양한 배가 있다.
보통 사람들은 문장의 맥락을 보면서 배의 의미를 파악한다. 하지만 배라는 글자만 주어지면 이게 어떤 것을 의미하는지 잘 모른다. 우리는 프로그래밍을 할 때 이렇게 중복되거나, 전체 코드를 봐야 의미를 알 수 있는 의미 없는 이름 사용을 자제해야 한다.
의도를 분명히 밝혀라
int d; // 경과시간(단위: 날짜)
int elapsedTimeDays;
int daysSinceCreation;
d라는 변수는 코드를 전부 읽어서 어디에 쓰이는지를 파악하기 전에는 아무도 어떤 용도로 쓰이는지 모른다.
하지만 elapsedTimeDays, daysSinceCreation은 이름에서 볼 수 있듯이 의도가 드러나서 코드 이해와 변경이 쉬워진다. 예시를 하나 더 들어보겠다.
public List<int[]> getThem(){
List<int[]> list1 = new ArrayList<int[]>();
for(int[] x : theList)
if(x[0] == 4)
list1.add(x);
return list1;
}
이렇게 보면 위에서 말했듯이 코드가 무슨 일을 하는지 파악하기 어렵다.
반복문을 알고 있는 개발자라면 이 코드가 어떻게 작동하는지 알 것이다. 하지만 이 코드가 왜 필요하며, 어떤 용도로 쓰이는지는 아무도 모를 것이다.
public List<Cell> getFlaggedCells(){
List<Cell> flaggedCells = new ArrayList<Cell<>();
for (Cell cell : gameBoard)
if(cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}
단순히 이름과 int 배열이 아닌 칸을 단순한 클래스로 만들어서 표현한 것만으로도 개발자의 의도가 훨씬 잘 드러남을 알 수 있다. 함수가 하는 일은 깃발이 꽂혀있는 Cell을 가져오는(get)하는 것이다.
그릇된 정보를 피하라
프로그래머는 코드에 그릇된 정보를 남겨서는 안된다.
프로그래밍을 전공하는 사람들에게 List는 특별한 의미다. 실제 List가 아니라면 accountList 등의 이름을 붙이지 않는다. 이럴 때는 accounts, accountGroup라고 이름을 붙인다.
또한 흡사한 이름을 사용하지 않도록 주의해야 한다. abcAddressController과 acAddressController는 모니터에 눈을 부릅뜨고 봐야 두 개의 차이가 보인다.
유사한 개념은 유사한 표기법을 사용한다.
일관성이 떨어지는 표기법은 그릇된 정보로 간주한다.
왜냐하면 최신 자바 환경은 코드 자동 완성 기능을 제공하는데 일관성이 떨어지는 표기법을 사용하면 코드 완성 기능을 사용함에 있어 불편함이 생기기 때문이다.
의미 있게 구분하라
컴파일러나 인터프리터만 통과하려는 생각으로 코드를 구현하는 프로그래머는 스스로 문제를 일으킨다.
동일한 범위 내에서는 다른 두 개념에게 같은 이름을 부여하지 못한다. 이때 통과만 하면 된다는 생각으로 철자만 살짝 바꾸거나, 연속된 숫자를 붙이거나 불용어를 추가하는 방식은 적절치 못하다.
코드로 예시를 들어보겠다.
public static void copyChars(char a1[], char a2[]){
for(int i = 0; i < a1.length; i++) {
a2[i] == a1[i];
}
}
이때 프로그래머는 통과하기 위해 a1, a2라는 char 배열을 사용했다.
비록 통과는 될지 몰라도 프로그래머는 이 메소드를 사용할 때 a1과 a2에 어떤 인자를 보내야 하는지 알기 위해 전체 코드를 봐야 한다.
public static void copyChars(char source[], char destination[]){
for (int i = 0; i < source.length; i++) {
destination[i] = source[i];
}
}
간단히 매개변수 이름을 변경하는 것만으로도 프로그래머는 다음에 이 메소드를 사용할 때 인자로 어떤 값을 넘겨줘야 하는지 쉽게 알 수 있다.
a1, a2와 같은 연속된 숫자를 덧붙이는 것도 안 되지만 불용어를 붙이는 것도 안 된다.zork라는 변수가 이미 있다고 해서 다른 용도로 쓰이는 변수에 theZork라는 이름을 붙여서는 안된다.
이와 비슷한 예시로 Product와 ProductData, ProductInfo가 있다. 이렇게 개념을 구분하지 않은 채 이름만 달리하는 경우는 프로그래밍에서 매우 지양한다. 읽는 사람이 차이를 알도록 이름을 지어라.
- 불용어(Stop word)는 분석에 큰 의미가 없는 단어를 지칭합니다
발음하기 쉬운 이름을 사용하라
genymdhms (generate date, year, month, day, hour, minute, second)라는 변수가 있다.
어떻게 읽을 것인가? 다른 개발자에게 이 변수를 사용하는 클래스, 메소드를 설명할 때 이 변수를 어떻게 읽을 것인가..
발음하기 쉬운 이름을 사용하는 것을 추천한다. 지적인 대화가 가능해진다.
검색하기 쉬운 이름을 사용하라
MAX_CLASSES_PER_STUDENT은 grep(Linux 파일 검색기)로 쉽게 찾을 수 있다.
하지만 e, 7 같은 이름은 찾기가 어렵다. e 또는 7이 들어간 모든 변수가 검색되기 때문이다.
그래서 긴 이름을 추천한다.
인코딩을 피하라
인코딩 : 코드에서 문자열이나 데이터를 특정 형식으로 변환하는 작업
과거 윈도우 C API는 헝가리식 표기법을 굉장히 중요하게 여겼다.
헝가리식 표기법은 변수 및 함수의 이름 인자 앞에 데이터 타입을 명시하는 코딩 규칙이다.
옛날 IDE는 현재 IDE처럼 친절하게 마우스만 갖다 대도 데이터 타입을 알려주지 않는다. 그래서 헝가리 식 표기법이 굉장히 중요했다.
하지만 현재는 IDE의 발전으로 인해 헝가리식 표기법은 방해가 될 뿐이다.
그리고 이제는 멤버 변수 앞에 m_을 붙이거나 학생 변수 앞에 s_을 붙일 필요가 없다.
클래스와 함수는 접두어가 필요 없을 정도로 작아야 한다 또한 접두어는 옛날에 작성한 구닥다리 코드라는 징표가 된다.
지금까지는 인코딩에 대해 안 좋은 말만 계속했는데 인코딩이 필요한 경우도 있다.
갑자기 인코딩이 필요한 경우가 있다고 하면 매우 혼란스러울 것이라고 생각한다.
인터페이스 클래스와 구현 클래스
인터페이스 클래스와 구현 클래스 패턴으로 설계해야 하는 이유는 객체지향의 5원칙 중 OCP원칙 때문이다.
OCP 원칙에 대해 간단히 설명을 하면 개방, 폐쇄 원칙이라고 하며 ‘소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다.’는 프로그래밍 원칙입니다.
인터페이스 클래스와 구현 클래스 이름이 똑같으면 구현 클래스의 이름에 Impl을 붙이는 것을 추천한다.
@Service
public class MainService {
ResponseEntity<?> doAction();
}
@Service
public class MainServiceImpl implements MainService {
@Override
public ResponseEntity<?> doAction() {
System.out.println(“do Action B”);
}
}
자신의 기억력을 자랑하지 마라
독자가 코드를 읽으면서 변수 이름을 자기가 아는 이름으로 변환해야 한다면 그 변수 이름은 바람직 하지 못하다.
왜냐하면 문제 영역이나 해법 영역에서 사용하지 않는 이름을 선택했기 때문에 생기는 문제다.
for (int i = 0; i<100; i++) // 이렇게 루프에서 사용하는 변수는 괜찮다.
int i = student.num; // 일반적인 경우에서는 문자 하나만 사용하는 변수는 바람직하지 않다.
남들이 이해하는 코드를 짜는 사람이 전문가이다.
클래스 & 메소드 이름
클래스 이름은 명사나 명사구가 적합하다. Customer, Account, Address, Member, User
메소드 이름은 동사나 동사구가 적합하다. Save, Add, ex
이때 접근자는 get, 변경자는 set, 조건자는 is를 붙인다. (자바와 한 약속)
생성자를 중복 정의할 때는 정적 팩토리 메소드를 이용한다.
- 정적 팩토리 메소드란?
- 개발자가 구상한 Static Method를 통해 간접적으로 생성자를 호출 하는 객체를 생성하는 디자인 패턴이다.
- 정적 팩토리 메소드의 장점
- 호출 될 때마다 인스턴스를 새로 생성하지 않아도 된다.
- 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 된다.
- 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
class Member {
private String name;
// 생성자를 private화 하여 외부에서 생성자 호출 차단
private Member(String name) { this.name = name; }
// 정적 팩토리 메소드
public static Member nameOf(String name) {
return new Member(name); // 메서드에서 생성자를 호출하고 리턴함
}
}
public static void main(String[] args) {
// 정적 메소드 호출을 통해 인스턴스화된 객체를 얻음
Member user = Member.titleOf("홍용준");
Member user_use_newKeyword = new Member("홍길동"); // 생성자가 private이므로 불가능
}
Member 클래스의 nameOf 메소드는 사용자가 설정한 Static Method(정적 팩토리 메소드)이다.
nameOf를 사용하면 인스턴스 객체를 만들때 생성자를 호출하지 않고 만들 수 있다.
말장난을 하지 마라
- 한 개념에 한 단어를 사용하라
추상적인 개념 하나에 단어 하나를 선택해 이를 고수한다.
똑같은 메소드를 클래스마다 fetch, retrieve, get으로 제각각 혼란스럽다.
Intellj, Eclipse는 객체를 사용하면 객체가 제공하는 메소드 목록을 보여준다. 이때 보통은 메소드 이름과 매개변수만 보여준다. 그래서 메소드 이름은 독자적이고 일관적이어야 한다. - 한 단어를 두 가지 목적으로 사용하지 마라
위에서 메소드 이름이 독자적이고 일관적이어야 한다고 해서 다른 목적으로 사용한데 굳이 일관적으로 사용할 필요가 없다. 예를 들어서 set이라는 메소드는 원래 있던 값을 변경하는 메소드인데 새로 작성하는 메소드는 새로운 값 하나를 추가한 뒤, 그 값을 부여하는 역할을 한다.
이때 일관성을 위해서 set이라는 이름을 사용하면 안 된다. add나 insert라는 이름을 사용해야 한다. - 해법 영역에서 가져온 이름을 사용하라
우리가 짠 코드를 보는 이들은 우리와 같은 프로그래머다. 그러므로 전문용어, 전산용어, 알고리즘 이름, 패턴 이름, 수학 용어를 사용해도 된다. AccountQueue라는 이름을 이해 못 하는 프로그래머는 없을 것이다.
그러므로 기술 개념에는 기술 이름이 가장 적합한 선택이다. - 문제 영역에서 가져온 이름을 사용하라
적절한 프로그래머 용어가 없으면 문제 영역에서 이름을 가져온다. 그러면 코드를 보수하는 프로그래머가 분야 전문가에게 의미를 물어 파악할 수 있다. - 의미 있는 맥락을 추가하라
firstName, lastName, street, houseNumber, city, zipcode라는 변수가 있다.
변수를 훑어보면 주소라는 사실을 금방 알아챌 수 있다.
하지만 firstName만 있는 경우에는 주소인 사실을 알아챌 수가 없다.
그래서 변수의 앞에 addr이라는 의미 있는 맥락을 추가할 수 있다. - 불필요한 맥락을 없애라
Gas Station Deluxe라는 애플리케이션을 짠다고 가정하자.
모든 클래스 이름을 GSD로 시작하겠다는 생각은 전혀 바람직하지 못하다.
Intellij에서는 G라고 검색을 하면 G로 시작하는 모든 클래스가 나오기 때문이다.
일반적으로는 짧은 이름이 긴 이름보다 좋다. 단, 의미가 분명한 경우에 한해서다.
public class Address {
private int firstName;
private int lastName;
private int zipCode;
}
public class member {
Address accountAddress = new Address();
}
Address로 예시를 들어보겠다.
accountAddress는 클래스의 인스턴스 이름으로 적당하고 Address는 클래스 이름으로 적당하다.
accountAddress에는 account라는 불필요한 맥락이 들어갔기 때문!