이 책은 세 부분으로 나눠진다.
처음 몇 장은 깨끗한 코드를 작성하는 원칙, 패턴, 실기를 설명한다. 코드가 많아 읽기가 힘들지도 모르지만 둘째 부분을 준비하는 단계다. 첫 부분만 읽고서 책을 내려놓는다면...... 행운을 빈다.
둘째 부분은 좀 더 어렵다. 여러 사례 연구를 소개하는데, 복잡도는 점점 더 높아진다. 각 사례 연구는 코드를 깨끗하게 고치는, 즉 문제가 있는 코드를 문제 가 더 적은 코드로 바꾸는 연습이다. 상세히 살펴보려면 집중력이 필요하다. 설명과 코드를 번갈아 뒤적여야 한다. 코드를 분석하고 이해하며 코드에 가하는 변경과 이유를 납득해야 한다. 그러려면 여러 날이 걸리므로 시간도 충분히 투자해야 한다.
셋째 부분은 결말이다. 사례 연구를 만들면서 수집한 냄새와 휴리스틱(heuristic) 을 마지막 장에서 열거한다. 사례 연구에서 코드를 분석하고 정리하면서 우리는 우리 행위의 모든 이유를 휴리스틱이나 냄새로 정리했다. 코드를 분석하고 고치며 우리가 느끼는 감정을 이해하려 애썼고, 그렇게 느끼는 이유와 그렇게 고치는 이유를 잡아내려 애썼다. 코드를 짜고, 읽고, 정리하는 관점에서, 우리가 생각하는 방식을 묘사한 지식 기반을 구축했다.
- 책 들어가는 글 중 일부 발췌
책 내용에 대한 짧막한 정리 위주로 작성하였습니다. 앞으로의 내용은 편의상 문어체로 작성하였습니다.
제가 느끼는 생각이나 보충하고 싶은 내용은 인용 구문을 활용하여 추가 작성하였습니다. (잘못된 내용이 있다면 피드백 부탁드리겠습니다.)
이름을 맥락에 맞게 수정하는 것은 정말로 어려운 것 같습니다. 기존에 통용되었던 단어여야 하고, 의미가 바로 읽혀야 하는 이름을 찾아야 하기 때문입니다. 그러기 위해선 코드가 어떻게 흘러가는지 맥락(context)를 이해해야 하기 때문에 더욱 어려움이 느껴졌던 것 같습니다. (지금도 어렵게 느낍니다......)
변수, 함수, 클래스 이름은 다음의 질문 모두 답해야 한다
따로 주석이 필요하다면 의도를 분명히 드러내지 못했다는 것
의도가 드러나는 이름의 예
int d; // 경과 시간 (단위: 날짜)
int elapsedTimelnDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgelnDays;
해독이 어려운 코드의 예
public List<int[]> getThem() {
List<int []> listl = new ArrayList<int[]>();
for (int[] x : theList)
if (x[0] = 4) listl.add(x);
return list1;
}
변수 변경
// 변경 전
public List<int[]> getFlaggedCells() {
List<int[]> flaggedCells = new ArrayList<int[]>();
for (int[] cell : gameBoard)
if (cell[STATUS_VALUE] = FLAGGED)
flaggedCells.add(cell);
return flaggedCells;
}
// 변경 후
public List<Cell> getFlaggedCells() {
List<Cell> flaggedCells = new ArrayList<int[]>();
for (Cell cell : gameBoard)
if (cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}
int[] 를 명시적인 객체로 변경하는 것으로써 함수가 하는 일, 함수가 다루는 객체를 대략적으로 이해 또는 대비 하고 코드를 작성할 수 있게 만들었다고 생각합니다.
List 객체 생성 시 접미사로 -List 로 붙였던 적이 많았기에 앞으로는 데이터 유형보다는 의미 집단이 표현되는 명칭을 사용할 수 있도록 의식하면서 만들어보고자 합니다.
서로 흡사한 이름을 사용하지 않도록 주의
소문자 L, 대문자 O 사용 시 헛갈리는 상황 발생
int a = 1;
if ( O == 1 )
a = Ol;
else
l = 01;
"알기 쉬운 이름으로 바꾸면 문제가 깨끗이 풀린다." 라고 나와 있으며 이름이 명확하지 않으면 결국 열어본 파일(또는 클래스) 상에서 해결할 수 없어 다른 정보를 찾아야 하기 때문에 일이 번거로워지기도 했습니다.
컴파일러나 인터프리터만 통과하려는 생각으로 코드를 구현하는 프로그래머는 스스로 문제를 일으킨다
class DtaRcrdl02 {
private Date genymdhms;
private Date modymdhms;
private final String pszqint = "102";
}
class Member {
private Date generationTimestamp;
private Date modificationTijnestamp;
private final String recordld = "102";
}
MAX_CLASSES_PER_STUDENT
와 7
중에 눈에 잘 띄는 것은...?책의 필자 (Tim Ottinger) 개인적으로 간단한 method에서 local variable만 한 문자를 사용한다고 합니다.
검색하기 쉬운 상수명의 차이
for (int j = 0; j < 34; j++) {
s += (t[j] * 4) / 5;
}
int realDaysPerldealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j = 0; j < NUMBER_OF_TASKS; j++) {
int realTaskDays = taskEstimate[j] * reaIDaysPerldealDay;
int realTaskWeeks = (realTaskDays / WORK_DAYS_PER_WEEK);
sum += realTaskWeeks;
}
함수의 라인은 길어지지만 결국 5를 사용하는 것과 WORK_DAYS_PER_WEEK 로 사용하는 것 중 의미 파악이 쉬운 것은 후자일 것입니다.
Data
,Info
는 보면서 뜨끔했던 내용이었던게 평상시에 해당 데이터의 부차정보에 대해서 -Info로 붙여 명칭을 지정한 적이 많았기 때문입니다.
java의 경우 클래스를 통해 객체단위를 형성하게 되므로 이름을 생각할 때 구현하고자 하는 객체를 생각하면서 명명하는 것이 이름 짓는데 도움이 된다고 합니다.
사실상 대상 클래스가 field나 method를 통해 객체의 상태, 행위, 책임에 대한 정보를 가지고 있기 때문에 class를 굳이 -Info라 명명하는 것는 중복적인 표현이면서 모호한 표현이라는 피드백을 받았습니다.
동사나 동사구가 적합
접근자(Accessor), 변경자(Mutator), 조건자(Predicate)는 javabean 표준에 따라 값 앞에 get, set, is를 붙임
생성자(Constructor)를 중복정의 (overload)할 때는 static factory method를 사용
// Complex fulcrumPoint = new Complex(23.0);
Complex fulcrumPoint = Complex.FromRealNumber(23.0);
코딩한 개발자 혼자 알고 있는 표현보다는 범용적으로 사용되는 단어, 표현을 사용하는 것이 좋다는 것으로 생각됩니다.
대충 훑어봐도 이해하기 편한 코드를 작성 하는 것이 목표!
구현에 필요한 내용과 연관된 객체명이 아니라면 기술적인 이름을 사용함으로써 해당 객체가 어떤 동작 또는 역할을 수행할 것인지 미리 짐작해볼 수 있을겁니다.
의미가 분명한 이름 > 클래스, 함수, 이름 공간에 넣어 맥락 부여 > 접두어를 붙여 의미 부여
// 일반적인 변수명의 예
firstName, lastName, street, houseNumber. city, state, zipcode // state 하나만 있다면?
// 주소라는 맥락을 이해할 수 있는 변수명의 예
addrFirstName, addrLastName, addrStreet, addrHouseNumber. addrCity, addrState, addrZipcode // addr 접두어롤 통해 address라는 맥락 확인 가능
// 만약 해당 변수가 class의 member라면 Address라는 맥략 파악이 더욱 용이
// 변수들이 좀 더 큰 개념에 속한다는 사실이 컴파일러에게도 분명해진다.
class Address{
...
}
맥락이 불분명한 변수
private void printGuessStatistics(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);
}
맥락이 분명한 변수
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);
verb = "are";
pluralModifier = "s";
}
private void thereIsOneLetter() {
number = "1";
verb = "is";
pluralModifier = "";
}
private void thereAreNoLetters() {
number = "no";
verb = "are";
pluralModifier = "s";
}
}
다른 곳에서도 쓰여야 하는 객체라면 특정 프로그램 또는 특정 위치(패키지) 로 제한하지 않는게 맥락 파악에 혼란이 없을 것 같습니다.
class instance로는 적합 이라는 내용은
정해진 규약을 따라 특정 모듈을 위해 작성된 객체임을 표기하는데 적합하다는 것으로 이해하였습니다.