| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- 1024
- 4장
- 문제풀이
- 프로그래머스
- dp
- Repository
- 수학
- 클린코드
- 자기호출
- 8장
- 6장
- 2장
- BOJ
- Clean code
- inductive step
- mirror
- 7장
- programmers
- 0장
- 원의 방정식
- 재귀
- recursion
- N으로 표현
- 규칙
- 백준
- basis step
- 기하학
- 3장
- 1011
- Wiki
- Today
- Total
LeeA0의 공부 일기
[Clean Code] 2장. 의미 있는 이름 본문
[사담]
이번 장부터 본격적인 클린 코드 작성방법이 나와서 좋았습니다.
코드의 예시까지 함께볼 수 있어서 좋았는데, 이해 안 되는 부분도 몇 있어서 그 부분만 여러 번 읽었네요 ㅎㅎ
규칙의 예시 중에 "젠 야무다 힘즈"가 너무 재밌었습니다..ㅋㅋㅋ
이름을 정할 땐 항상 조심해야겠네요~ㅎ
[정리]
<이름을 잘 짓는 간단한 규칙>
1. 의도를 분명히 밝혀라
변수나 함수, 클래스 이름은 다음과 같은 질문에 답해야 한다.
- 존재 이유는?
- 수행 기능은?
- 사용 방법은?
(따로 주석이 필요하다면 의도를 분명히 드러내지 못했다는 의미
<안 좋은 예시 1>
int d; // 경과 시간(단위:날짜)
<좋은 예시 1>
int daysSinceCreation;
int daysSiceModification;
<안 좋은 예시 2>
public List<int[]> getThem() {
List<int[]> list1 = new ArraysList<int[]>();
for (int[] x : theList)
if (x[0] == 4)
list1.add(x);
return list1;
}
위 예시를 알아보려면 다음과 같은 정보를 알아야 한다.
- theList에 뭐가 들었는가?
- theList에서 0번째 값이 어째서 중요한가?
- 값 4는 무슨 의미인가?
- 함수가 반환하는 리스트 list1을 어떻게 사용하는가?
=> '지뢰 찾기 게임을 만든다고 가정, theList가 게임판, 배열에서 0번째 값을 깃발이 꽂힌 상태'로 가정
<좋은 예시 2>
public List<int[]> getFlaggedCells() {
List<int[]> flaggedCells = new ArraysList<int[]>();
for (int[] cell : gameBoard)
if (cell[STATUS_VALUE] == FLAGGED)
flaggedCells.add(cell);
return flaggedCells;
}
이걸 더 수정해본다면, (Cell이라는 객체를 만들어 int[]를 대체, isFlagged라는 메서드를 만들어 상수를 감춤)
public List<Cell> getFlaggedCells() {
List<Cell> flaggedCells = new ArraysList<int[]>();
for (Cell cell : gameBoard)
if (cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}
2. 그릇된 정보를 피하라
- 나름대로 의미 있는 단어를 다른 의미로 사용하면 안 됨
ex) hp, aix, sco
-> 유닉스 플랫폼이나 유닉스 변종을 가리키는 이름)
ex) accountList
-> 여러 계정을 그룹으로 묶을 때, 실제 List가 아니다.
-> 프로그래머에게 List라는 단어는 특수한 의미
-> accountGroup, bunchOfAccounts, Accounts가 낫다.
- 서로 흡사한 이름을 사용하지 않도록 주의
ex) XYZControllerForEfficientHandlingOfStrings / XYZControllerForEfficientStorageOfStrings
- 소문자 L (l)이나 대문자 O를 피해라
int a = l;
if ( O == 1)
a = O1;
else
l = 01;
3. 의미 있게 구분하라
- 컴파일러나 인터프리터만 통과하려는 생각으로 코드를 구현하는 것 X
ex) class라는 변수 -> klass (끔찍..)
- 연속된 숫자를 덧붙이거나 불용어를 추가하는 방식은 적절하지 않음
ex) a1, a2, ..., aN
ex) ProductInfo 혹은 ProductData -> Product와 다를 게 없다.
그 밖에도 NameString/Name, theZork/zork, Customer/CustomerObject, ...
<안 좋은 예시>
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];
}
}
4. 발음하기 쉬운 이름을 사용하라
ex) genymdhms; generate date, year, month, day, hour, minute, second
-> 회의 때마다 "젠 와이 엠 디 에이취 엠 에스" -> "젠 야 무다 힘즈"가 돼버리는 불상사가 발생한다.
-> generationTimestamp (좀 더 지적인 대화가 가능해진다)
5. 검색하기 쉬운 이름을 사용하라
- 문자 하나를 사용하는 이름 혹은 상수는 검색하기 어렵다.
ex) 7 -> 7이 들어가는 모든 파일 이름, 수식이 다 검색된다.
MAX_CLASSES_PER_STUDENT = 7 -> 이런 식으로 사용하면 변경도 쉽고, 검색도 쉽다.
- 이름 길이는 (변수)범위 크기에 비례해야 한다.
<안 좋은 예시>
for (int j=0; i<34; j++) {
s += (t[j]*4)/5;
}
<좋은 예시>
int realDaysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j=0; j < NUMBER_OF_TASKS; j++) {
int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
int realTaskWeeks = (realTaskDays / WORK_DAYS_PER_WEEK);
sum += realTaskWeeks;
}
-> 그냥 5보다 WORK_DAYS_PER_WEEK가 검색하기 훨씬 편하다!
6. 인코딩을 피하라
- 유형이나 범위 정보까지 인코딩에 넣으면 이름을 해독하기 어려워짐
ex) 헝가리식 표기법
-> 윈도 C API - 정수 핸들, long 포인터, void 포인터, ...
-> 옛날에는 컴파일러가 타입을 점검하지 않아 프로그래머에게 타입을 기억할 단서가 필요했음
-> Java 프로그래머는 이럴 필요가 없다.
(객체는 강한 타입이며, IDE는 코드를 컴파일하지 않고도 타입 오류를 감지한다.)
<좋은 예시>
PhoneNumber phoneString;
-> 타입이 바뀌어도 이름이 바뀌진 않는다!
ex) 멤버 변수 접두어
-> 멤버 변수에 m_이라는 접두어를 붙일 필요 없다.
-> 멤버 변수를 다른 색상으로 표시하거나 눈에 띄게 보여주는 IDE를 사용!
<안 좋은 예시>
public class Part {
private String m_dsc;
void setName(String name) {
m_dsc = name;
}
}
<좋은 예시>
public class Part {
private String description;
void setDescription(String description) {
this.description = description;
}
}
7. 인터페이스 클래스와 구현 클래스
- 인터페이스 이름에 접두어를 붙이지 않는 편이 좋음
ex) 도형을 생성하는 ABSTRACT FACTORY
| 인터페이스 클래스 Interface Class |
구체 클래스 Concreate Class |
| IShapeFactory ( X ) | ShapeFactory |
| ShapeFactory | ShapeFactoryImp |
| ShapeFactory | CShapeFactory |
8. 자신의 기억력을 자랑하지 마라
- 독자가 코드를 읽으면서 변수 이름을 자신이 아는 이름으로 변환해야 한다면 바람직하지 못한 변수명임
- 문제 영역이나 해법 영역에서 사용하는 이름을 선택
- 문자 하나만 사용하는 변수 이름은 문제가 있음
but. 루프에서 반복 횟수를 세는 i, j, k는 가능 (l은 안되요!)
-> 루프에서 반복 횟수 변수는 전통적으로 한 글자를 사용하기 때문
-> a, b를 사용했으니 c를 선택한다는 억지 논리는 금물!
- 명료함이 최고
ex)
| GOOD | BAD |
| 명료함이 최고인 전문가 프로그래머 |
r이 호스트와 프로토콜을 제외한 소문자 URL라는 것을 알고있는 똑똑한 프로그래머 |
- 클래스 이름은 명사나 명사구가 적합, Manager/Processor/Data/Info 등 사용 기피
- 메서드 이름은 동사나 동사구가 적합, 접근자 -> get, 변경자 -> set, 조건자 -> is를 붙임
<예시>
String name = employee.getName();
customer.setName("mike");
if (paycheck.isPosted()) ...
- 생성자를 정복정의할 때는 정적 팩토리 메서드를 사용
<안 좋은 예시>
Complex fulcrumPoint = new Complex(23.0);
<좋은 예시>
Complex fulcrumPoint = Complex.FromRealNumber(23.0);
9. 기발한 이름은 피하라
- 재미난 이름보다 명료한 이름
- 특정 문화에서만 사용하는 농담은 피해라
| GOOD | BAD |
| DeleteItems | HolyHandGrenade |
| kill() | whack() |
| Abort() | eatMyShort() |
10. 한 개념에 한 단어를 사용하라
- 추상적인 개념 하나에 단어 하나를 선택해 이를 고수
ex1) fetch, retrieve, get
ex2) controller, manager, driver
11. 말장난을 하지 마라
- 한 단어를 두 가지 목적으로 사용하지 말 것
ex)
| 기존 값 두 개를 더하거나 이어서 새로운 값을 만듬 |
집합에 값 하나 추가 |
| add | add ( X ) |
| add | insert나 append ( O ) |
12. 해법 영역에서 가져온 이름을 사용하라
- 코드를 읽을 사람도 프로그래머
- 전산 용어, 알고리즘 이름, 패턴 이름, 수학 용어 등을 사용해도 괜찮음
ex) JobQueue, VISITOR패턴 -> AccountVisitor
13. 문제 영역에서 가져온 이름을 사용하라
- 적절한 '프로그래머 용어'가 없다면 문제 영역에서 이름을 가져옴
14. 의미 있는 맥락을 추가하라
- 클래스, 함수, 이름 공간에 넣어 맥락을 부여
- 모든 방법이 실패하면 최후의 수단으로 접두어를 붙임
ex)
| firstName, lastName, street, houseNumber, city, state, zipcode |
-> 전체를 보면 주소라는 사실을 알 수 있다.
but. state라는 변수 하나만 사용한다면?
-> addr라는 접두어를 추가해 맥락을 분명히 한다.
| addrFirstName, addrLastName, addrState, ... |
-> Address라는 클래스를 생성하면 더 좋다.
<안 좋은 예시>
private void printGuessStatistics(char candidate, int count) {
String number;
String verb;
String pluralModifier;
if (count == 0) {
number = "no";
verv = "are";
pluralModifier = "s";
} else if (count == 1) {
number = "1";
verv = "is";
pluralModifier = "";
} else {
number = Integer.toString(count);
verv = "are";
pluralModifier = "s";
}
String guessMessage = String.format(
"There %s %s %s%s", verb, number, candidate, pluralModifier
);
print(guessMessage);
}
-> 함수를 끝까지 읽어야 통계 추측 메시지에 사용된다는 것을 알 수 있다.
-> 함수가 좀 길으므로 함수를 작은 조각으로 쪼갠다.
< 좋은 예시>
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);
verv = "are";
pluralModifier = "s";
}
private void thereIsOneLetter() {
number = "1";
verv = "is";
pluralModifier = "";
}
private void thereAreNoLetters() {
number = "no";
verv = "are";
pluralModifier = "s";
}
}
15. 불필요한 맥락을 없애라
ex) 고급 휘발유 충전소; Gas Station Deluxe 애플리케이션
-> 모든 클래스 이름을 GSD로 시작? -> G를 입력하고 자동 완성 키를 입력하면 모든 클래스가 나와버린다..!
-> accountAddress와 customerAddress는 Address 클래스 인스턴스로는 좋지만, 클래스 이름으로는 적합하지 못함
-> Account는 클래스 이름으로 적합!
-> 만약, 포트 주소, MAC 주소, 웹 주소를 구분해야 한다면 PostalAddress, MAX, URI라는 이름도 괜찮다.
여느 코드 개선 노력과 마찬가지로 이름 역시 나름대로 바꿨다가는 누군가 질책할지도 모른다.
그렇다고 코드를 개선하려는 노력을 중단해서는 안된다.
|
|
![]() |
|
'Java > Clean Code' 카테고리의 다른 글
| [Clean Code] 3장. 함수 (미완) (0) | 2021.09.11 |
|---|---|
| [Clean Code] 2장 - 실습 (0) | 2021.09.02 |
| [Clean Code] 1장. 깨끗한 코드 (0) | 2021.08.22 |
| [Clean Code] 0장. 들어가면서 (0) | 2021.08.22 |
| [Clean Code] 시작일기 (0) | 2021.08.22 |
