이 글은 개발자 필독서인 클린 코드를 읽으며 습득한 내용을 정리한 글입니다. 모든 출처는 해당 저서에 있습니다.
의도가 분명한 이름은 중요하다.
좋은 이름을 짓는 데 걸리는 시간보다 앞으로 절약할 수 있는 시간이 더 많다.
변수•함수•클래스 이름을 지을 때 고려해야 할 사항들은 다음과 같다.
주석이 필요하다면 의도를 분명히 드러내지 못한 것이다.
/* 아무런 의미가 드러나지 않음 */
int d; // 경과 시간(단위: 날짜 수)
/* 측정하려는 값과 단위를 표현 */
int elapsedTimeInDays;
의도가 드러나는 이름을 사용하면 코드 이해와 변경이 쉬워진다.
/* 코드가 하는 일을 짐작하기 어려움 */
public List<int[]> getThem() {
List<int[]> list1 = new ArrayList<>();
// theList에 무엇이 들었는가?
for (int[] x : theList) {
// theList에서 0번째 값이 어째서 중요한가? 값 4는 무슨 의미인가?
if (x[0] == 4) {
list1.add(x);
}
}
return list1; // 함수가 반환하는 리스트 list1을 어떻게 사용하는가?
}
💡 코드 맥락이 코드 자체에 명시적으로 드러나지 않음(코드의 함축성)/**
* 게임판 : theList → gameBoard
* 0번째 값 : 상태
* 값 4 : 깃발이 꽂힌 상태
*/
public List<int[]> getFlaggedCells() {
List<int[]> flaggedCells = new ArrayList<int[]>();
for (int[] cell : gameBoard) {
if (cell[STATUS_VALUE] == FLAGGED) {
flaggedCells.add(cell);
}
}
return flaggedCells;
}
💡 코드가 더욱 명확해짐/**
* 배열 대신 클래스 사용(int 배열 → Cell 클래스)
* 상수 대신 함수 사용(FLAGGED → isFlagged)
public List<Cell> getFlaggedCells() {
List<Cell> flaggedCells = new ArrayList<Cell>();
for (Cell cell : gameBoard) {
if (cell.isFlagged()) {
flaggedCells.add(cell);
}
}
return flaggedCells;
}
💡 함수가 하는 일을 이해하기 쉬워짐코드에 그릇된 단서를 남겨서는 안 된다. 그릇된 단서는 코드 의미를 흐린다.
hp
: 유닉스 플랫폼/변종을 가리키는 이름 → 변수 이름으로 적합하지 않음XYZControllerForEfficientHandlingOfStrings
와 XYZControllerForEfficientStorageOfStrings
int a = l;
if (O == l) {
a = 01;
} else {
l = 01;
}
→ 소문자 L은 숫자 1로, 대문자 O는 숫자 0으로 보임이름이 달라야 한다면 의미도 달라져야 한다. 읽는 사람이 차이를 알도록 이름을 지어라.
public static void copyChars(char a1[], char a2[]) {
for (int i = 0; i < a1.length; i++) {
a2[i] = a1[i];
}
}
public static void copyChars(char source[], char destination[]) {
for (int i = 0; i < source.length; i++) {
destination[i] = source[i];
}
}
문자 하나를 사용하는 이름과 상수는 텍스트 코드에서 쉽게 눈에 띄지 않는다.
전문가 프로그래머는 자신의 능력을 좋은 방향으로 사용해 남들이 이해하는 코드를 내놓는다.
메소드 이름은 동사나 동사구를 사용한다.
ex) postPayment, deletePage, save 등
접근자, 변경자, 조건자는 자바 빈 표준에 따라 값 앞에 get
, set
, is
를 붙인다.
String name = employee.getName();
customer.setName("mike");
if (paycheck.isPosted()) { ... }
생성자를 중복해 정의할 때(overload)는 정적 팩토리 메소드를 사용한다.
// 좋은 예
Complex fulcrumPoint = Complex.FromRealNumber(23.0);
// 나쁜 예
Complex fulcrumPoint = new Complex(23.0);
의도를 분명하고 솔직하게 표현해야 한다.
firstName → addrFirstName
lastName → addrLastName
state → addrState
맥락이 불분명한 변수
private void printGuessStaticstics(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);
}
number
, verb
, pluralModifier
라는 변수의 의미가 불분명하다.맥락이 분명한 변수
public class GuessStatisticMessage {
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) {
therAreNoLetters();
} 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() {
numberr = "no";
verb = "are";
pluralModifier = "s";
}
}
GuessStatisticsMessage
라는 클래스를 만들어 세 변수를 클래스에 넣음으로써 세 변수의 맥락이 분명해졌다.의미가 분명한 경우에 한해 짧은 이름이 긴 이름보다 낫다. 중복 등 불필요한 맥락을 추가하지 않도록 주의한다.
예시 1) 클래스 이름
xxxAddress(x), Address(o)
예시 2) 주소 구분
MAC 주소 : MAC
포트 주소 : PortAddress
웹 주소 : URI
📖 참고
- 로버트 C. 마틴, 『Clean Code 클린 코드 애자일 소프트웨어 장인 정신』, 박재호·이해영 옮김, 케이앤피북스(2010), p53-69.